﻿using AJWPFAdmin.Core;
using AJWPFAdmin.Core.Components.AJTable.ViewModels;
using AJWPFAdmin.Core.Components.ViewModels;
using AJWPFAdmin.Core.Components.Views;
using AJWPFAdmin.Core.Enums;
using AJWPFAdmin.Core.ExtensionMethods;
using AJWPFAdmin.Core.GlobalEvents;
using AJWPFAdmin.Core.HardwareSDKS.EQLED;
using AJWPFAdmin.Core.HardwareSDKS.Models;
using AJWPFAdmin.Core.Logger;
using AJWPFAdmin.Core.Models;
using AJWPFAdmin.Core.Models.EF.Tables;
using AJWPFAdmin.Core.Mvvm;
using AJWPFAdmin.Core.Utils;
using AJWPFAdmin.Modules.Common.ViewModels;
using AJWPFAdmin.Modules.Common.Views;
using AJWPFAdmin.Modules.Main.Views;
using AJWPFAdmin.Services;
using AJWPFAdmin.Services.AppointAPI;
using AJWPFAdmin.Services.AppointAPI.GoodsInfoQuery;
using AJWPFAdmin.Services.EF;
using AJWPFAdmin.Services.EF.TableExts;
using AJWPFAdmin.Services.Models;
using ClosedXML.Excel;
using DnsClient.Protocol;
using DocumentFormat.OpenXml.Spreadsheet;
using DocumentFormat.OpenXml.Wordprocessing;
using Masuit.Tools;
using Masuit.Tools.Reflection;
using Masuit.Tools.Systems;
using MaterialDesignThemes.Wpf;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Query.Internal;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Bcpg.OpenPgp;
using Org.BouncyCastle.Utilities;
using Org.BouncyCastle.Utilities.Encoders;
using Prism.Commands;
using Prism.Events;
using Prism.Services.Dialogs;
using Quartz.Util;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Net.Http;
using System.Printing;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Timers;
using System.Web;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using Yitter.IdGenerator;
using static AJWPFAdmin.Core.ExceptionTool;
using static AJWPFAdmin.Modules.Main.ViewModels.CarIdentificationPanelViewModel;
using static AJWPFAdmin.Modules.Main.ViewModels.DeviceScreenViewModel;
using static AJWPFAdmin.Services.AJMQTTService;

namespace AJWPFAdmin.Modules.Main.ViewModels
{
    public enum ConfirmPassCmdParameter
    {
        Dialog,
        Ignore,
        ManualPass,
        ForcePass
    }

    public class PassagewayDirectionBrushOrIconConverter : IValueConverter
    {
        private static readonly Brush _green = (Brush)Application.Current.FindResource("AJGreen");
        private static readonly Brush _red = (Brush)Application.Current.FindResource("AJRed");

        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value is PassagewayDirection d)
            {
                return "icon".Equals(parameter?.ToString())
                    ? d == PassagewayDirection.进 ? MaterialDesignThemes.Wpf.PackIconKind.CarArrowLeft : MaterialDesignThemes.Wpf.PackIconKind.CarArrowRight
                    : d == PassagewayDirection.进 ? _green : _red;
            }
            return "icon".Equals(parameter?.ToString()) ? MaterialDesignThemes.Wpf.PackIconKind.CarArrowLeft : _green;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return null;
        }
    }

    public class CarTypeEnumVisibleConverter : IValueConverter
    {
        public bool HiddenWhenEqualParameter { get; set; }

        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value is long id)
            {
                if (Enum.TryParse<CarTypeEnum>(parameter?.ToString(), out var type))
                {
                    return id == (long)type
                        ? (HiddenWhenEqualParameter ? Visibility.Collapsed : Visibility.Visible)
                        : (HiddenWhenEqualParameter ? Visibility.Visible : Visibility.Collapsed);
                }

            }
            return Visibility.Collapsed;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return null;
        }
    }

    public class PassagewayDirectionToVisibleConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value is PassagewayDirection d)
            {
                if (parameter is string tag && !string.IsNullOrWhiteSpace(tag))
                {
                    return d == Enum.Parse<PassagewayDirection>(tag) ? Visibility.Visible : Visibility.Collapsed;
                }

            }
            return Visibility.Collapsed;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return null;
        }
    }

    public class CarIdentificationPanelViewModel : ViewModelBase
    {

        private List<CarImageDownloadProcessMap> _carImageDownloadProcessMap;

        /// <summary>
        /// 手动处理台账记录对话框是否打开
        /// </summary>
        private bool _isShippingRecordFormDialogOpen;

        private EventHandler<ShippingRecord> _carArrivedEvent;

        private ObservableCollection<string> _processMessages;
        /// <summary>
        /// 顶部消息处理消息列表
        /// </summary>
        public ObservableCollection<string> ProcessMessages
        {
            get { return _processMessages; }
            set { SetProperty(ref _processMessages, value); }
        }

        private ObservableCollection<string> _deviceNotifyQueueCheckMessages;
        /// <summary>
        /// 设备通知队列处理消息列表
        /// </summary>
        public ObservableCollection<string> DeviceNotifyQueueCheckMessages
        {
            get { return _deviceNotifyQueueCheckMessages; }
            set { SetProperty(ref _deviceNotifyQueueCheckMessages, value); }
        }

        private bool _bottomMessageDrawerOpen;
        public bool BottomMessageDrawerOpen
        {
            get { return _bottomMessageDrawerOpen; }
            set { SetProperty(ref _bottomMessageDrawerOpen, value); }
        }

        private string _apiLogs;
        public string APILogs
        {
            get => _apiLogs;
            set => SetProperty(ref _apiLogs, value);
        }

        private PassagewayDirection _direction;
        public PassagewayDirection Direction
        {
            get => _direction;
            set => SetProperty(ref _direction, value);
        }

        private string _latestMessage;
        public string LatestMessage
        {
            get => _latestMessage;
            set => SetProperty(ref _latestMessage, value);
        }

        private bool _detailMessageDrawerOpen;
        public bool DetailMessageDrawerOpen
        {
            get { return _detailMessageDrawerOpen; }
            set { SetProperty(ref _detailMessageDrawerOpen, value); }
        }

        private bool _saving;
        public bool Saving
        {
            get { return _saving; }
            set { SetProperty(ref _saving, value); }
        }

        private bool _ajMQTTConnected;
        public bool AJMQTTConnected
        {
            get { return _ajMQTTConnected; }
            set { SetProperty(ref _ajMQTTConnected, value); }
        }

        private IDialogService _dialogSvc;
        private IEventAggregator _eventAggregator;
        private DbService db;
        private DeviceReceiveCmdEvent _deviceReceiveCmdEvent;
        private DeviceSetupProgressEvent _deviceSetupEvent;
        private CarIdentificationPassedEvent _carIdentificationPassedEvent;
        private AJLog4NetLogger _logger;

        private bool _tempRecordProcessWorking;
        private int _tempRecordProcessRemainCount;

        private SemaphoreSlim _deviceNotifyProcessLocker = new SemaphoreSlim(1, 1);

        private AppointAPISvc _appointSvc;
        private AJConfigService _cfgSvc;
        private UserControl _ctrl;
        private IList<DeviceInfo> _deviceSource;

        private Car _tempCarInfo;

        private ShippingRecord _curRecord;
        /// <summary>
        /// 当前正在处理的记录数据
        /// </summary>
        public ShippingRecord CurRecord
        {
            get { return _curRecord; }
            set { SetProperty(ref _curRecord, value); }
        }

        private int _queueCount;
        public int QueueCount
        {
            get { return _queueCount; }
            set { SetProperty(ref _queueCount, value); }
        }

        private DelegateCommand _openDetailMessageDrawerCmd;
        public DelegateCommand OpenDetailMessageDrawerCmd =>
            _openDetailMessageDrawerCmd ?? (_openDetailMessageDrawerCmd = new DelegateCommand(ExecuteOpenDetailMessageDrawerCmd));

        void ExecuteOpenDetailMessageDrawerCmd()
        {
            DetailMessageDrawerOpen = true;
        }

        private DelegateCommand _openBottomMessageDrawerCmd;
        public DelegateCommand OpenBottomMessageDrawerCmd =>
            _openBottomMessageDrawerCmd ?? (_openBottomMessageDrawerCmd = new DelegateCommand(ExecuteOpenBottomMessageDrawerCmd));

        void ExecuteOpenBottomMessageDrawerCmd()
        {
            BottomMessageDrawerOpen = true;
        }

        private DelegateCommand<UserControl> _controlLoadedCmd;
        public DelegateCommand<UserControl> ControlLoadedCmd =>
            _controlLoadedCmd ?? (_controlLoadedCmd = new DelegateCommand<UserControl>(ExecuteControlLoadedCmd));

        void ExecuteControlLoadedCmd(UserControl parameter)
        {
            _ctrl = parameter;
        }

        private Func<string, string> _customLEDTextFormatHandle;

        public CarIdentificationPanelViewModel(DbService dbIns,
            AJConfigService cfgSvc,
            IDialogService dialogSvc,
            IEventAggregator eventAggregator,
            AJLog4NetLogger logger,
            AJMQTTService aJMQTTService,
            AppointAPISvc appointSvc)
        {
            APILogs = "暂无接口日志";
            ProcessMessages = new ObservableCollection<string>();
            LatestMessage = "欢迎使用";
            ProcessMessages.CollectionChanged += (_, __) =>
            {
                if (ProcessMessages.Any())
                {
                    LatestMessage = ProcessMessages.FirstOrDefault();
                }
            };
            DeviceNotifyQueueCheckMessages = new ObservableCollection<string>();
            db = dbIns;
            _cfgSvc = cfgSvc;
            _dialogSvc = dialogSvc;
            _eventAggregator = eventAggregator;

            _deviceNotifyQueue = new Queue<DeviceInfo>();
            _eventAggregator.GetEvent<DeviceNotifyEvent>().Subscribe(OnDeviceNotify);
            _deviceReceiveCmdEvent = _eventAggregator.GetEvent<DeviceReceiveCmdEvent>();
            _carIdentificationPassedEvent = _eventAggregator.GetEvent<CarIdentificationPassedEvent>();
            //_client = new HttpClient();

            _deviceSetupEvent = _eventAggregator.GetEvent<DeviceSetupProgressEvent>();
            _deviceSetupEvent.Subscribe((progress) =>
             {
                 if (progress.Loading)
                 {
                     return;
                 }
                 _deviceSource = progress.Devices;
             });

            //_eventAggregator.GetEvent<ApplicationExitEvent>().Subscribe(async () =>
            //{
            //    //_client.Dispose();
            //    //await _aJMQTTService.CloseAsync(true);
            //});
            _logger = logger;
            _appointSvc = appointSvc;
            _appointSvc.API.APILogsRaised += (_, eventArgs) =>
            {
                if (Direction == eventArgs.Direction)
                {
                    APILogs += eventArgs.Logs;
                }
            };
            //_aJWebSvcConfig = new AJWebSvcConfig();
            //// 暂时写死使用初始化默认值
            //_aJWebSvcConfig.Init();
            //_aJMQTTService = aJMQTTService;
            //_eventAggregator.GetEvent<AJMQTTSvcStatusChangedEvent>().Subscribe(OnAJMQTTSvcStatusChanged);
            //Task.Run(ConnectToMQTTSvc);
            _carArrivedEvent += OnCarArrived;

            //开启一个长线程,从设备通知队列读取数据处理
            Task.Factory.StartNew(CheckDeviceNotifyQueue, TaskCreationOptions.LongRunning);

            _eventAggregator.GetEvent<AJGlobalExceptionResolvedEvent>().Subscribe(() =>
            {
                if (CurRecord != null && !_isShippingRecordFormDialogOpen)
                {
                    if (_tempRecordProcessWorking)
                    {
                        ReleaseLocker();
                    }

                }
                //// 如果没有打开手动对话框，则顶掉当前车
                //if (!_isShippingRecordFormDialogOpen)
                //{
                //    // 并且队列来了一个车，则当前车直接通过(忽略),让下一个车识别通知进来
                //    if (_deviceNotifyQueue.Count > 0)
                //    {
                //        var carTypeEnum = (CarTypeEnum)CurRecord.TypeId;

                //        var cmdParam = ConfirmPassCmdParameter.Ignore;

                //        if (carTypeEnum == CarTypeEnum.进出厂车 || carTypeEnum == CarTypeEnum.固定车)
                //        {
                //            cmdParam = ConfirmPassCmdParameter.ForcePass;
                //        }
                //        _ctrl.Dispatcher.Invoke(() => ConfirmPassAsync(cmdParam));
                //    }
                //    else
                //    {
                //        if (_tempRecordProcessWorking)
                //        {
                //            ReleaseLocker();
                //        }
                //    }
                //}
            });

            _customLEDTextFormatHandle = (txtTemplate) =>
            {
                if (string.IsNullOrWhiteSpace(txtTemplate) || !txtTemplate.Contains("[进出次数]"))
                {
                    return txtTemplate;
                }
                using var db = DbService.GetDbContext(_cfgSvc);
                var carNo = CurRecord?.CarNo;
                var count = db.ShippingRecords
                .Count(p => p.ShipStartDate != null && p.ShipEndDate != null && p.CarNo == carNo);
                return txtTemplate.Replace("[进出次数]", count.ToString());
            };

            _carImageDownloadProcessMap = new List<CarImageDownloadProcessMap>
            {
                new() {
                    PropertyName = nameof(Car.Attachments),
                    Desc = "随车清单"
                },
                new ()
                {
                    PropertyName = nameof(Car.VehicleLicense),
                    Desc = "行驶证"
                },
                new ()
                {
                    PropertyName = nameof(Car.CarImage),
                    Desc = "车辆图片"
                },
                new ()
                {
                    PropertyName = nameof(Car.Nameplate),
                    Desc = "整车铭牌"
                },
                new ()
                {
                    PropertyName = nameof(Car.EngineNameplate),
                    Desc = "发动机铭牌"
                },
                new ()
                {
                    PropertyName = nameof(Car.MachineEnvProtectionLabel),
                    Desc = "环保信息标签"
                },
                new ()
                {
                    PropertyName = nameof(Car.EmissionStandardImages),
                    Desc = "排放阶段图片"
                }
            };


            ////TODO: 调试用，模拟5s后触发一个识别, 调试完记得注释
            //System.Timers.Timer tm = new System.Timers.Timer
            //{
            //    Interval = 5 * 1000
            //};
            //var count = 0;
            //tm.Elapsed += (_, __) =>
            //{
            //    tm.Stop();
            //    OnDeviceNotify(new DeviceInfo
            //    {
            //        IP = "192.168.8.188",
            //        CarIdentificationPlateResult = new CarIdentificationResult
            //        {
            //            CarNo = "冀B04005D"
            //        }
            //    });
            //    count++;

            //};
            //tm.Start();

        }

        //private async void ConnectToMQTTSvc()
        //{
        //    await _aJMQTTService.ConnectAsync(_aJWebSvcConfig.IP, _aJWebSvcConfig.MQTTPort);
        //}

        //private void OnAJMQTTSvcStatusChanged(bool status)
        //{
        //    AJMQTTConnected = status;
        //}

        private EntryConfig GetEntryConfig(DbService db)
        {
            var cfgJson = db.SystemConfigDictionaries
                    .Where(p => p.Key == Core.Enums.SystemConfigKey.EntryConfig)
                    .Select(p => p.StringValue).FirstOrDefault();

            var entryConfig = CommonUtil.TryGetJSONObject<EntryConfig>(cfgJson) ?? new EntryConfig();
            if (entryConfig.EmissionStandardCfgItems.Count == 0)
            {
                entryConfig.InitDefault();
            }
            return entryConfig;
        }
        private void OnCarArrived(object sender, ShippingRecord e)
        {
            _ctrl.Dispatcher.Invoke(async () =>
            {
                CurRecord = e;
                OpenShippingRecordDialogCmd.RaiseCanExecuteChanged();
                if (CurRecord.IsConfigAutoPass)
                {
                    await ConfirmPassAsync(ConfirmPassCmdParameter.ForcePass);
                }
                else
                {
                    var ledConfig = GetLEDConfig(db);
                    OpenGate(CurRecord, ledConfig, false);
                    DisplayLEDAsync(CurRecord, ledConfig, LEDTextScene.正在通行);
                }
            });

        }

        private async void OnDeviceNotify(DeviceInfo deviceInfo)
        {
            var device = _deviceSource?.FirstOrDefault(p => p.IP == deviceInfo.IP);
            if (device == null || device.Direction != this.Direction)
            {
                return;
            }

            var carNo = deviceInfo.CarIdentificationPlateResult.CarNo;

            APILogs += $"\r\n已识别到 {carNo}\r\n";

            if (_deviceNotifyProcessLocker.CurrentCount == 0)
            {
                APILogs += $"尚未释放识别线程！\r\n";
            }

            if (deviceInfo is not SioReaderDevice && string.IsNullOrWhiteSpace(deviceInfo.CarIdentificationPlateResult.FullImgFile))
            {
                APILogs += $"未能获取车牌识别图片,请重新{Direction}厂\r\n";
                return;
            }

            await _deviceNotifyProcessLocker.WaitAsync();

            APILogs += $"锁定识别线程！\r\n";

            using var db = DbService.GetDbContext(_cfgSvc);

            var ip = deviceInfo.IP;

            var dbDevice = await db.Devices.AsNoTracking().FirstOrDefaultAsync(p => p.IP == ip);

            if (dbDevice == null)
            {
                _deviceNotifyProcessLocker.Release();
                return;
            }

            deviceInfo.Id = dbDevice.Id;
            deviceInfo.WatchhouseId = dbDevice.WatchhouseId;
            deviceInfo.WatchhouseName = dbDevice.WatchhouseName;
            deviceInfo.PassagewayId = dbDevice.PassagewayId;
            deviceInfo.PassagewayName = dbDevice.PassagewayName;

            var watchhouseId = dbDevice.WatchhouseId;
            var watchhouse = db.Watchhouses.Where(p => p.Id == watchhouseId).AsNoTracking().FirstOrDefault();

            var passagewayId = dbDevice.PassagewayId;
            var passageway = db.Passageways.Where(p => p.Id == passagewayId).AsNoTracking().FirstOrDefault();

            if (watchhouse == null || passageway == null)
            {
                _deviceNotifyProcessLocker.Release();
                return;
            }

            deviceInfo.Direction = passageway.Direction;

            if (passageway.Direction != this.Direction)
            {
                _deviceNotifyProcessLocker.Release();
                return;
            }

            if (_deviceNotifyQueue.Count > 0)
            {
                // 如果有同道闸同车号的数据， 说明重复了， 不能继续加入队列
                if (_deviceNotifyQueue.Any(p => p.PassagewayId == deviceInfo.PassagewayId
                && p.CarIdentificationPlateResult.CarNo == carNo))
                {
                    _deviceNotifyProcessLocker.Release();
                    return;
                }
            }

            _deviceNotifyQueue.Enqueue(deviceInfo);
            _deviceNotifyProcessLocker.Release();
            APILogs += $"{carNo} 已入队列,释放识别线程\r\n";

        }

        private void OnDeviceNotifyDequeue(DeviceInfo deviceInfo)
        {
            //todo-xq
            _tempRecordProcessWorking = true;
            _tempRecordProcessRemainCount = 30;

            if (!_cfgSvc.IsAuthorized)
            {
                _ctrl.Dispatcher.Invoke(() =>
                {
                    ProcessMessages.Insert(0, $"未激活或试用已到期:{deviceInfo.CarIdentificationPlateResult.CarNo}");
                });
                ReleaseLocker();
                return;
            }

            var db = DbService.GetDbContext(_cfgSvc);

            var carNo = deviceInfo.CarIdentificationPlateResult.CarNo;
            var ip = deviceInfo.IP;

            var dbDevice = db.Devices.AsNoTracking().FirstOrDefault(p => p.IP == ip);

            if (dbDevice == null)
            {
                _ctrl.Dispatcher.Invoke(() =>
                {
                    ProcessMessages.Insert(0, $"未找到匹配的设备信息：{deviceInfo.IP}");
                });
                ReleaseLocker();
                db.Dispose();
                return;
            }

            var watchhouseId = dbDevice.WatchhouseId;
            var watchhouse = db.Watchhouses.Where(p => p.Id == watchhouseId).AsNoTracking().FirstOrDefault();

            var passagewayId = dbDevice.PassagewayId;
            var passageway = db.Passageways.Where(p => p.Id == passagewayId).AsNoTracking().FirstOrDefault();

            if (watchhouse == null || passageway == null)
            {
                _ctrl.Dispatcher.Invoke(() =>
                {
                    ProcessMessages.Insert(0, $"关联进出口或道闸信息缺失：{deviceInfo.IP}");
                });
                ReleaseLocker();
                db.Dispose();
                return;
            }

            var isReaderDevice = deviceInfo is SioReaderDevice;

            var isNewCar = false;

            var carInfo = db.Cars
                .IfWhere(() => isReaderDevice, p => p.CardNo == carNo)
                .IfWhere(() => !isReaderDevice, p => p.CarNo == carNo)
                .FirstOrDefault();

            if (carInfo != null)
            {
                carNo = carInfo.CarNo;
            }
            else if (isReaderDevice)
            {
                _ctrl.Dispatcher.Invoke(() =>
                {
                    ProcessMessages.Insert(0, $"卡号对应的车辆信息不存在：{carNo}");
                });
                ReleaseLocker();
                db.Dispose();
                return;
            }

            // 60秒内不允许再次出入,防止识别到车尾牌号导致重复记录 --阿吉 2024年7月17日09点59分
            var latestRecord = db.ShippingRecords.Where(p => p.CarNo == carNo).OrderByDescending(p => p.CreateDate).AsNoTracking().Select(p => new
            {
                p.CarNo,
                p.CreateDate
            }).FirstOrDefault();

            if (latestRecord != null && Convert.ToInt32(DateTime.Now.Subtract(latestRecord.CreateDate).TotalSeconds) < 60)
            {
                _ctrl.Dispatcher.Invoke(() =>
                {
                    ProcessMessages.Insert(0, $"{carNo} 需要超过1分钟后才能{Direction}厂");
                });
                ReleaseLocker();
                db.Dispose();
                return;
            }

            var entryConfig = GetEntryConfig(db);

            var processTask = new Task(() =>
            {
                var carType = db.CarTypes.Where(p => p.Id == carInfo.TypeId).AsNoTracking().FirstOrDefault();

                if (carType == null)
                {
                    _ctrl.Dispatcher.Invoke(() =>
                    {
                        ProcessMessages.Insert(0, $"无法处理 {carNo}: 无关联车类型,请检查系统配置");
                    });
                    ReleaseLocker();
                    db.Dispose();
                    return;
                }

                // 检测排放标准,国五及其一下车辆进厂时间对不对 --阿吉 2024年10月22日11点51分
                var belowOrEqualStandard5CanPass = true;
                if (carInfo.EmissionStandard <= EmissionStandard.国五
                && Direction == PassagewayDirection.进)
                {
                    var now = DateTime.Now;

                    var lessThanStart = false;
                    var startDate = entryConfig.BelowOrEqualStandard5PassStartTime.GetValueOrDefault();
                    if (startDate != DateTime.MinValue
                    && now < now.Date.AddHours(startDate.Hour).AddMinutes(startDate.Minute).AddSeconds(startDate.Second))
                    {
                        lessThanStart = true;
                    }

                    var lagerThanEnd = false;
                    var endDate = entryConfig.BelowOrEqualStandard5PassEndTime.GetValueOrDefault();
                    if (entryConfig.BelowOrEqualStandard5PassEndTimeInNextDay)
                    {
                        now = now.Date.AddDays(1);
                    }
                    if (endDate != DateTime.MinValue
                    && now > now.Date.AddHours(endDate.Hour).AddMinutes(endDate.Minute).AddSeconds(endDate.Second))
                    {
                        lagerThanEnd = true;
                    }
                    if (lessThanStart || lagerThanEnd)
                    {
                        belowOrEqualStandard5CanPass = false;
                    }
                }

                if (!belowOrEqualStandard5CanPass)
                {
                    var canNotPassMsg = $"管控时段,禁止通行";
                    _ctrl.Dispatcher.Invoke(() =>
                    {
                        ProcessMessages.Insert(0, canNotPassMsg);
                    });

                    var ledConfig = GetLEDConfig(db);

                    var tempRcd = new ShippingRecord
                    {
                        CarNo = carInfo.CarNo,
                        CreateDate = DateTime.Now,
                        EmissionStandard = carInfo.EmissionStandard,
                    };

                    var txtArray = ledConfig.FormatLEDTextArray(LEDTextScene.正在通行, Direction, tempRcd, false);

                    txtArray[txtArray.Length - 1] = canNotPassMsg;

                    ledConfig.DisplayToLEDs(LEDTextScene.正在通行, passageway, tempRcd, false, txtArray);

                    ReleaseLocker();
                    db.Dispose();

                    return;
                }

                var typeIdEnum = (CarTypeEnum)((int)carInfo.TypeId);
                //todo-xq
                switch (typeIdEnum)
                {
                    case CarTypeEnum.进出厂车:
                    case CarTypeEnum.固定车:
                    case CarTypeEnum.非道路移动机械:
                        ProcessLedger(carInfo, deviceInfo, typeIdEnum, watchhouse, passageway, dbDevice, isNewCar);
                        break;
                    case CarTypeEnum.临时车:
                    case CarTypeEnum.免检车:
                        ProcessNotLedger(carInfo, deviceInfo, typeIdEnum, watchhouse, passageway, dbDevice, isNewCar);
                        break;
                    default:
                        ReleaseLocker();
                        db.Dispose();
                        break;
                }
            });

            Action createTempCarInfo = () =>
            {
                isNewCar = true;
                // 生成临时车数据
                _tempCarInfo = carInfo = new Car
                {
                    Id = YitIdHelper.NextId(),
                    CarNo = carNo,
                    TypeId = (long)CarTypeEnum.临时车,
                    TypeName = CarTypeEnum.临时车.ToString(),
                    CreateDate = DateTime.Now,
                    PlateColor = deviceInfo.CarIdentificationPlateResult.Color
                };
            };

            if (carInfo == null)
            {
                isNewCar = true;
            }

            var queryCarInfo = entryConfig.AppointCheck;

            var goodsQueryTemplate = _appointSvc.API.GetGoodsInfoQueryTemplate(db).QueryTemplate;

            // 如果是台账车,判断基础资料全不全
            if (!isNewCar)
            {
                if (carInfo.TypeId <= (long)CarTypeEnum.固定车)
                {
                    var checkCarInfoTask = Task.Run(async () =>
                    {
                        var checkCarInfoResult = await _appointSvc.API.CheckCarInfoAsync(new CarInfoParams
                        {
                            CarInfo = carInfo,
                        });
                        return !checkCarInfoResult.Success;
                    });
                    checkCarInfoTask.Wait();

                    queryCarInfo = checkCarInfoTask.Result;
                }

                // 再判断是不是启用了物资地磅对接，没有启用的话也强制使用小程序查询 --阿吉 2024年12月7日10点55分
                if (!queryCarInfo)
                {
                    queryCarInfo = goodsQueryTemplate == GoodsInfoQueryTemplate.无.ToString();
                }
            }
            //todo-xq
            if (queryCarInfo)
            {
                var carInfoWorker = CreateCarInfoWorker(carNo, passageway,
                    (car) =>
                    {
                        if (car == null)
                        {
                            // 接口车辆为空,接口报错或未查询到数据
                            // 数据库也没有,创建临时车变量
                            if (isNewCar)
                            {
                                createTempCarInfo();
                            }
                        }
                        else
                        {
                            // 接口车辆不为空,表示小程序接口查询到了数据,
                            // 数据库没查询到数据
                            // 这里将小程序返回的车辆信息赋值给临时车变量, 然后直接升级成进出厂车
                            if (isNewCar)
                            {
                                _tempCarInfo = carInfo = car;
                                carInfo.PlateColor = deviceInfo.CarIdentificationPlateResult.Color;
                                carInfo.TypeId = (long)CarTypeEnum.进出厂车;
                                carInfo.TypeName = CarTypeEnum.进出厂车.ToString();
                            }
                            else
                            {
                                // 数据库有数据
                                // 小程序接口也有数据
                                // 用小程序接口的数据赋值到数据库车辆
                                // 有些数据不变
                                // 如果是本地是临时车,小程序查询到数据了, 那么升级成进出场车 --阿吉 2025年4月18日10点09分
                                if (carInfo.TypeId == (long)CarTypeEnum.临时车)
                                {
                                    carInfo.LocalTempLevelupToLedger = true;
                                    carInfo.TypeId = (long)CarTypeEnum.进出厂车;
                                    carInfo.TypeName = CarTypeEnum.进出厂车.ToString();
                                }
                                car.TypeId = carInfo.TypeId;
                                car.TypeName = carInfo.TypeName;
                                car.PlateColor = carInfo.PlateColor;
                                car.UpdateDate = DateTime.Now;
                                car.InGoodsName = carInfo.InGoodsName;
                                car.InGoodsType = carInfo.InGoodsType;
                                car.InGoodsVolume = carInfo.InGoodsVolume;
                                car.OutGoodsName = carInfo.OutGoodsName;
                                car.OutGoodsType = carInfo.OutGoodsType;
                                car.OutGoodsVolume = carInfo.OutGoodsVolume;

                                var needUpdateImages = false;
                                if (!string.IsNullOrWhiteSpace(carInfo.Attachments)
                                && carInfo.Attachments != car.Attachments)
                                {
                                    needUpdateImages = true;
                                }

                                if (!string.IsNullOrWhiteSpace(carInfo.VehicleLicense)
                                && carInfo.VehicleLicense != car.VehicleLicense)
                                {
                                    needUpdateImages = true;
                                }

                                if (!string.IsNullOrWhiteSpace(carInfo.CarImage)
                                && carInfo.CarImage != car.CarImage)
                                {
                                    needUpdateImages = true;
                                }

                                if (!string.IsNullOrWhiteSpace(carInfo.Nameplate)
                                && carInfo.Nameplate != car.Nameplate)
                                {
                                    needUpdateImages = true;
                                }

                                if (!string.IsNullOrWhiteSpace(carInfo.EngineNameplate)
                                && carInfo.EngineNameplate != car.EngineNameplate)
                                {
                                    needUpdateImages = true;
                                }

                                if (!string.IsNullOrWhiteSpace(carInfo.MachineEnvProtectionLabel)
                                && carInfo.MachineEnvProtectionLabel != car.MachineEnvProtectionLabel)
                                {
                                    needUpdateImages = true;
                                }

                                if (!string.IsNullOrWhiteSpace(carInfo.EmissionStandardImages)
                                && carInfo.EmissionStandardImages != car.EmissionStandardImages)
                                {
                                    needUpdateImages = true;
                                }

                                CommonUtil.CopyPropertyValues(carInfo, car);
                                db.Entry(carInfo).State = EntityState.Modified;

                                if (needUpdateImages)
                                {
                                    // 更新关联车辆台账记录的车辆行驶证和随车清单数据
                                    db.ShippingRecords.Where(p => p.CarNo == carNo)
                                    .ExecuteUpdate(sp => sp.SetProperty(p => p.Attachments, carInfo.Attachments)
                                    .SetProperty(p => p.VehicleLicense, carInfo.VehicleLicense)
                                    .SetProperty(p => p.CarImage, carInfo.CarImage)
                                    .SetProperty(p => p.Nameplate, carInfo.Nameplate)
                                    .SetProperty(p => p.EngineNameplate, carInfo.EngineNameplate)
                                    .SetProperty(p => p.MachineEnvProtectionLabel, carInfo.MachineEnvProtectionLabel)
                                    .SetProperty(p => p.EmissionStandardImages, carInfo.EmissionStandardImages));
                                }

                                db.SaveChanges();
                            }
                        }
                        processTask.Start();
                    });
                carInfoWorker.RunWorkerAsync();
            }
            else
            {
                if (isNewCar)
                {
                    createTempCarInfo();
                }
                processTask.Start();
            }

        }

        private BackgroundWorker CreateQueryGoodsInfoWorker(Passageway passageway,
            Func<Car> carInfoHandler, Action<Car, WeighingDataCacheExt> completeHandler)
        {
            var queryGoodsInfoWorker = new BackgroundWorker
            {
                WorkerReportsProgress = true,
            };
            var carInfo = carInfoHandler();
            queryGoodsInfoWorker.DoWork += (_, e) =>
            {
                _ctrl.Dispatcher.Invoke(() =>
                {
                    ProcessMessages.Insert(0, $"{carInfo.CarNo} 查询{Direction}厂货物信息");
                });

                var queryGoodsInfoTask = Task.Run(async () =>
                {
                    return await _appointSvc.API.GetGoodsInfoAsync(new CarInfoParams
                    {
                        CarInfo = carInfo,
                        Passageway = passageway,
                    });
                });

                queryGoodsInfoTask.Wait();

                var goodsInfoResult = queryGoodsInfoTask.Result;

                if (!goodsInfoResult.Success)
                {
                    _ctrl.Dispatcher.Invoke(() =>
                    {
                        APILogs += $"\r\n查询{Direction}厂货物信息失败:{goodsInfoResult.Message}";
                        ProcessMessages.Insert(0, $"{carInfo.CarNo} 查询{Direction}厂货物信息失败");
                    });
                }
                else
                {
                    var goodsInfo = goodsInfoResult.Data as WeighingDataCacheExt;
                    goodsInfo?.FillToCar(ref carInfo);
                    e.Result = goodsInfo;
                }
            };
            queryGoodsInfoWorker.RunWorkerCompleted += (_, e) =>
            {
                if (e.Error != null)
                {
                    _ctrl.Dispatcher.Invoke(() =>
                    {
                        ProcessMessages.Insert(0, $"{carInfo.CarNo} 查询物资异常：{e.Error.Message}");
                    });
                    completeHandler(null, null);
                    return;
                }
                _ctrl.Dispatcher.Invoke(() =>
                {
                    ProcessMessages.Insert(0, $"{carInfo.CarNo} 开始{Direction}厂");
                });
                completeHandler(carInfo, e.Result as WeighingDataCacheExt);
            };
            return queryGoodsInfoWorker;
        }

        public BackgroundWorker CreateCarLimitQueryWorker(Func<Car> carInfoHandler,
            Action<bool> completeHandler, Func<ShippingRecord> tempRecordHandler)
        {
            var end = DateTime.Today;
            var start = end.AddDays(90).Date;
            var carInfo = carInfoHandler();
            var carLimitQueryWorker = new BackgroundWorker { WorkerReportsProgress = true };
            // -1 出错或者中断
            var carLimitQueryProgress = 0;

            // 黑名单接口异常或者网络问题, 也要走流程
            var blackQueryError = false;

            var carNo = carInfo.CarNo;

            carLimitQueryWorker.DoWork += (_, e) =>
            {
                using var db = DbService.GetDbContext(_cfgSvc);
                var apiKey = _appointSvc.API.Config.Key;


                #region queryExcessiveEmissionsList

                var emiRiseRcd = db.EmiRiseRecords.AsNoTracking()
                .FirstOrDefault(p => p.API == apiKey
                && (p.ExceedFlag > 1 || p.ExceedFlag < 1)
                && p.CarNo == carNo);

                if (emiRiseRcd != null)
                {
                    carLimitQueryWorker.ReportProgress(carLimitQueryProgress, $"{_appointSvc.API.KeyDescription} 排放超标车辆接口提示:{emiRiseRcd.ExceedInfo}");
                    carLimitQueryProgress = -1;
                    e.Result = emiRiseRcd.ExceedInfo;
                    return;
                }

                carLimitQueryWorker.ReportProgress(carLimitQueryProgress, $"{carInfo.CarNo} 排放未超标");

                #endregion

                #region queryBlackListAsync

                var queryBlackListAsyncTask = Task.Run(async () =>
                {
                    return await _appointSvc.API.QueryBlackListAsync(new CarInfoParams
                    {
                        CarInfo = carInfo
                    });

                });

                queryBlackListAsyncTask.Wait();

                if (!queryBlackListAsyncTask.Result.Success)
                {
                    if (queryBlackListAsyncTask.Result.Data is Exception ex)
                    {
                        _logger.Error($"{_appointSvc.API.KeyDescription} 黑名单车辆接口提示", ex);
                    }
                    carLimitQueryWorker.ReportProgress(carLimitQueryProgress, $"{_appointSvc.API.KeyDescription} 黑名单车辆接口提示:{queryBlackListAsyncTask.Result.Message}");
                    blackQueryError = true;
                    e.Result = queryBlackListAsyncTask.Result.Message;
                    return;
                }
                if (queryBlackListAsyncTask.Result.Data is bool black && black)
                {
                    carLimitQueryWorker.ReportProgress(carLimitQueryProgress, $"{_appointSvc.API.KeyDescription} 黑名单车辆接口提示:{carInfo.CarNo} 已被列入黑名单");
                    carLimitQueryProgress = -1;
                    e.Result = $"{carInfo.CarNo} 已被列入黑名单";
                    return;
                }

                carLimitQueryWorker.ReportProgress(carLimitQueryProgress, $"{carInfo.CarNo} 非黑名单车辆");

                #endregion

            };

            carLimitQueryWorker.ProgressChanged += (_, e) =>
            {
                _ctrl.Dispatcher.Invoke(() =>
                {
                    var msg = e.UserState as string;
                    ProcessMessages.Insert(0, msg);
                    APILogs += $"{msg}\r\n";
                });
            };

            carLimitQueryWorker.RunWorkerCompleted += (_, e) =>
            {
                if (carLimitQueryProgress == -1)
                {
                    var record = tempRecordHandler();
                    if (e.Result is string err)
                    {
                        record.SyncResult = err;
                    }
                    TriggerAlarm(record);
                }
                if (carLimitQueryProgress == -1)
                {
                    ReleaseLocker();
                    db.Dispose();
                    return;
                }
                completeHandler(blackQueryError);

            };

            return carLimitQueryWorker;
        }

        private BackgroundWorker CreateCarInfoWorker(string carNo, Passageway passageway, Action<Car> successHandler)
        {
            var carInfoWorker = new BackgroundWorker
            {
                WorkerReportsProgress = true
            };

            var carInfo = new Car
            {
                Id = YitIdHelper.NextId(),
                CarNo = carNo,
                TypeId = (long)CarTypeEnum.临时车,
                TypeName = CarTypeEnum.临时车.ToString(),
                CreateDate = DateTime.Now,
            };

            carInfoWorker.DoWork += (_, e) =>
            {
                _ctrl.Dispatcher.Invoke(() =>
                {
                    APILogs = $"正在获取车辆信息...\r\n";
                    ProcessMessages.Insert(0, $"{carInfo.CarNo} 查询车辆信息...");
                });

                var carInfoParams = new CarInfoParams
                {
                    CarInfo = carInfo,
                    Passageway = passageway
                };
                var task = Task.Run(async () =>
                {
                    return await _appointSvc.API.GetCarInfoAsync(carInfoParams);
                });

                task.Wait();

                var tempCarInfo = task.Result.Data as Car;

                if (task.Result.Success)
                {
                    var carIdentificationConfig = CarIdentificationConfigExt.GetCarIdentificationConfig(db);

                    var targetCarNoImagesPath = CarIdentificationConfig.GetDefaultSavePath();

                    if (!string.IsNullOrWhiteSpace(carIdentificationConfig?.ImageSavePath))
                    {
                        targetCarNoImagesPath = carIdentificationConfig.ImageSavePath;
                    }

                    Action<string> webUploadLogHandler = null;
                    if (carIdentificationConfig.Type == CarIdentificationConfig.StoreType.网络)
                    {
                        webUploadLogHandler = (log) =>
                        {
                            carInfoWorker.ReportProgress(0, log);
                        };
                    }

                    _carImageDownloadProcessMap.ForEach((i) =>
                    {
                        i.Init(tempCarInfo);
                    });

                    var fileMaps = _carImageDownloadProcessMap.Where(p => !string.IsNullOrEmpty(p.File)).ToList();

                    if (fileMaps.Count > 0)
                    {
                        var dir = Path.Combine(targetCarNoImagesPath, carNo);

                        if (carIdentificationConfig.Type == CarIdentificationConfig.StoreType.本地)
                        {
                            try
                            {
                                if (!Directory.Exists(dir))
                                {
                                    Directory.CreateDirectory(dir);
                                }
                            }
                            catch (Exception err)
                            {
                                carInfoWorker.ReportProgress(0, $"{carInfo.CarNo} 无法下载图片:{err.Message}");
                                //fileMaps = Array.Empty<string>();
                            }
                        }

                        var downloadSuccess = 0;
                        foreach (var fileMap in fileMaps)
                        {
                            var fName = Path.GetFileName(fileMap.File);
                            var file = Path.Combine(dir, fName);

                            // 如果是本地存储模式
                            if (carIdentificationConfig.Type == CarIdentificationConfig.StoreType.本地)
                            {
                                // 如果已经存在本地图片, 则跳过
                                if (File.Exists(file))
                                {
                                    // 只存储文件名了,后续读取图片全部动态拼接, {管理端配置路径}/{车牌号}/xxx.jpg --阿吉 2025年1月10日10点20分
                                    fileMap.FillFileToEntity(ref tempCarInfo, fName);
                                    downloadSuccess++;
                                    carInfoWorker.ReportProgress(0, $"{carInfo.CarNo} {fileMap.Desc} 已下载!");
                                    continue;
                                }
                            }

                            // 如果不是网络图片,那可能有点问题,直接返回
                            if (!CommonRegex.HTTPURLREGEX.IsMatch(fileMap.File))
                            {
                                carInfoWorker.ReportProgress(0, $"{carInfo.CarNo} {fileMap.Desc} 下载失败,非网络图片!");
                                continue;
                            }

                            // 下载网络图到临时文件
                            using var stream = CommonUtil.GetImageStreamFromLocalOrHttp(fileMap.File, out var downloadErr);

                            if (stream == null)
                            {
                                carInfoWorker.ReportProgress(0, $"{carInfo.CarNo} {fileMap.Desc} 下载失败:{downloadErr}");
                                fileMap.FillFileToEntity(ref tempCarInfo, string.Empty);
                                continue;
                            }

                            var tempDir = Path.Combine(Path.GetTempPath(), carNo);
                            if (!Directory.Exists(tempDir))
                            {
                                Directory.CreateDirectory(tempDir);
                            }
                            var tempFile = Path.Combine(tempDir, fName);

                            try
                            {
                                using var fs = new FileStream(tempFile, FileMode.OpenOrCreate, FileAccess.ReadWrite);
                                stream.CopyTo(fs);
                                fs.Flush();
                            }
                            catch (Exception ex)
                            {
                                carInfoWorker.ReportProgress(0, $"{carInfo.CarNo} {fileMap.Desc} 下载失败:{ex.Message}");
                                fileMap.FillFileToEntity(ref tempCarInfo, string.Empty);
                                continue;
                            }

                            if (carIdentificationConfig.Type == CarIdentificationConfig.StoreType.本地)
                            {
                                if (CommonUtil.TryCopyFile(tempFile, file, out var errMsg))
                                {
                                    // 只存储文件名了,后续读取图片全部动态拼接, {管理端配置路径}/{车牌号}/xxx.jpg --阿吉 2025年1月10日10点20分
                                    fileMap.FillFileToEntity(ref tempCarInfo, fName);
                                    downloadSuccess++;
                                    carInfoWorker.ReportProgress(0, $"{carInfo.CarNo} {fileMap.Desc} 已下载!");
                                }
                                else
                                {
                                    carInfoWorker.ReportProgress(0, $"{carInfo.CarNo} {fileMap.Desc} 下载失败:{errMsg}");
                                }
                            }
                            else
                            {
                                var (uploadedFile, success) = carIdentificationConfig.UploadToWebService(tempFile,
                                    new Dictionary<string, string>
                                {
                                    {"category", carNo },
                                }, webUploadLogHandler);

                                if (success)
                                {
                                    file = uploadedFile;

                                    fileMap.FillFileToEntity(ref tempCarInfo, file);

                                    downloadSuccess++;
                                    carInfoWorker.ReportProgress(0, $"{carInfo.CarNo} {fileMap.Desc} 已下载!");
                                }
                            }

                        }
                        if (downloadSuccess > 0)
                        {
                            task.Result.Data = tempCarInfo;
                        }
                    }
                }

                e.Result = task.Result;
            };

            carInfoWorker.ProgressChanged += (_, e) =>
            {
                _ctrl.Dispatcher.Invoke(() =>
                {
                    var msg = e.UserState as string;
                    ProcessMessages.Insert(0, msg);
                    APILogs += $"{msg}\r\n";
                });
            };

            carInfoWorker.RunWorkerCompleted += (_, e) =>
            {
                if (e.Error != null)
                {
                    _ctrl.Dispatcher.Invoke(() =>
                    {
                        ProcessMessages.Insert(0, $"{carInfo.CarNo} 查询数据异常:{e.Error.Message}");
                    });
                    successHandler(null);
                    return;
                }
                var carRes = e.Result as ProcessResult;
                if (!carRes.Success)
                {
                    _ctrl.Dispatcher.Invoke(() =>
                    {
                        ProcessMessages.Insert(0, $"{carInfo.CarNo} {carInfo.TypeName} 未查询到数据");
                    });
                }
                successHandler(carRes.Data as Car);
            };

            return carInfoWorker;
        }

        private BackgroundWorker CreateUploadRecordWorker(Action<ProcessResult> completedHandler)
        {
            var worker = new BackgroundWorker();

            worker.DoWork += (_, e) =>
            {
                var record = e.Argument as ShippingRecord;

                //录制视频, 如果平台实现的话 --阿吉  2025年4月11日17点37分
                var videoCaptureTask = Task.Run(async () =>
                {
                    var passageId = record.Direction == PassagewayDirection.进
                    ? record.PassagewayId : record.OutPassagewayId.GetValueOrDefault();
                    return await _appointSvc.API.CaptureVideoAsync(new CaptureVideoParams
                    {
                        Record = record,
                        HIKDevice = _deviceSource.FirstOrDefault(p => p is HIKVisionDevice
                        && p.Direction == record.Direction
                        && p.PassagewayId == passageId),
                    });
                });

                videoCaptureTask.Wait();

                var videoRet = videoCaptureTask.Result;
                var videoFileName = videoRet.Data as string;
                if (videoRet.Success && !string.IsNullOrWhiteSpace(videoFileName))
                {
                    if (record.Direction == PassagewayDirection.进)
                    {
                        record.InVideoCaptureFile = CommonUtil.AJSerializeObject(new string[] { HttpUtility.UrlDecode(videoFileName) });
                    }
                    else
                    {
                        record.OutVideoCaptureFile = CommonUtil.AJSerializeObject(new string[] { HttpUtility.UrlDecode(videoFileName) });
                    }
                }

                _ctrl.Dispatcher.Invoke(() =>
                {
                    APILogs += "正在上传台账...\r\n";
                    ProcessMessages.Insert(0, $"{record.CarNo} 上传出入台账至 {_appointSvc.API.KeyDescription}");
                });

                var resultTask = Task.Run(async () =>
                {
                    return await _appointSvc.API.UploadShippingRecordAsync(new ShippingRecordParams
                    {
                        Record = record,
                        Operation = DataOperationType.新增,
                    });
                });
                resultTask.Wait();

                var uploadRet = resultTask.Result;

                if (uploadRet.Success && uploadRet.Attach is UploadShippingRecordResponse res)
                {
                    res.VideoFile = videoFileName;
                    uploadRet.Attach = res;
                }

                e.Result = uploadRet;
            };

            worker.RunWorkerCompleted += (_, e) =>
            {
                var result = e.Result as ProcessResult;
                if (e.Error != null)
                {
                    result = new ProcessResult();
                    result.SetError(e.Error.Message);
                }
                completedHandler(result);
            };
            return worker;
        }

        private BackgroundWorker CreateUploadCarInfoWorker(DbService db,
            Func<Car> carInfoHandler,
            Func<Passageway> passagewayHandler,
            Func<Device> deviceHandler,
            Action<ProcessResult> successHandler)
        {
            var carInfo = carInfoHandler();
            var passageway = passagewayHandler();
            var device = deviceHandler();

            // 如果接口返回卡车类型有重型,且车牌识别颜色是绿牌, 则要重新赋值车牌颜色, 兼容旧版相机无法正确识别车牌颜色
            if (!string.IsNullOrWhiteSpace(carInfo.TruckTypeName)
            && carInfo.TruckTypeName.Contains("重型") && carInfo.PlateColor == CarPlateColor.新能源绿牌)
            {
                carInfo.PlateColor = CarPlateColor.新能源黄牌;
            }

            var uploadCarWorker = new BackgroundWorker();

            uploadCarWorker.DoWork += (_, e) =>
            {
                _ctrl.Dispatcher.Invoke(() =>
                {
                    ProcessMessages.Insert(0, $"{carInfo.CarNo} 同步车辆台账信息至 {_appointSvc.API.KeyDescription}");
                });

                var task = Task.Run(async () =>
                {
                    return await _appointSvc.API.UploadCarInfoAsync(new CarInfoParams
                    {
                        CarInfo = carInfo,
                        Operation = carInfo.Synchronized ? DataOperationType.更新 : DataOperationType.新增,
                        Passageway = passageway,
                        Device = device
                    });
                });

                task.Wait();

                e.Result = task.Result;
            };

            uploadCarWorker.RunWorkerCompleted += (_, e) =>
            {
                if (e.Error != null)
                {
                    successHandler(new ProcessResult
                    {
                        Success = false,
                        Message = e.Error.Message,
                        Data = e.Error
                    });
                    return;
                }
                successHandler(e.Result as ProcessResult);
            };

            return uploadCarWorker;
        }

        private void ProcessLedger(
            Car carInfo,
            DeviceInfo deviceInfo,
            CarTypeEnum type,
            AJWPFAdmin.Core.Models.EF.Tables.Watchhouse watchhouse,
            AJWPFAdmin.Core.Models.EF.Tables.Passageway passageway,
            AJWPFAdmin.Core.Models.EF.Tables.Device dbDevice,
            bool isNewCar)
        {
            var db = DbService.GetDbContext(_cfgSvc);
            var autoPass = false;

            var enqueueTask = new Task(() =>
            {
                var entryConfig = GetEntryConfig(db);

                // 车类型是否自动
                var carTypeAuto = passageway.Direction == PassagewayDirection.进
                        ? entryConfig.CarTypeEnumCfgItems.Any(p => p.CarType == type && p.InChecked)
                        : entryConfig.CarTypeEnumCfgItems.Any(p => p.CarType == type && p.OutChecked);

                // 如果是进出厂车,还要判断排放标准
                if (type == CarTypeEnum.进出厂车)
                {
                    var standardAuto = entryConfig.EmissionStandardCfgItems.Any(p => p.Standard == carInfo.EmissionStandard && p.Checked);

                    autoPass = carTypeAuto && standardAuto;
                }
                else
                {
                    autoPass = carTypeAuto;
                }

                if (carInfo.Synchronized)
                {
                    var carLimitQueryWorker = CreateCarLimitQueryWorker(() => carInfo,
                        (blackQueryError) =>
                        {
                            var record = CreateTempRecord(db, carInfo, deviceInfo, watchhouse, passageway, dbDevice, autoPass);
                            record.BlackQueryError = blackQueryError;
                            _carArrivedEvent.Invoke(this, record);
                            db.Dispose();
                        }, () => CreateTempRecord(db, carInfo, deviceInfo, watchhouse, passageway, dbDevice, autoPass));

                    carLimitQueryWorker.RunWorkerAsync();
                }
                else
                {
                    // 如果经过车辆上传流程后车辆台账同步未成功,则强制改为手动进出
                    // 且不是默认接口的话, 则强制为手动处理,否则则按照管理端配置来 --阿吉 2025年4月21日20点33分
                    if (_appointSvc.API is not DefaultAppointAPI)
                    {
                        autoPass = false;
                    }

                    var record = CreateTempRecord(db, carInfo, deviceInfo, watchhouse, passageway, dbDevice, autoPass);
                    _carArrivedEvent.Invoke(this, record);
                    db.Dispose();
                }
            });

            _ctrl.Dispatcher.Invoke(() =>
            {
                ProcessMessages.Insert(0, $"开始处理 {carInfo.CarNo}");
            });

            if (passageway.Direction == PassagewayDirection.出)
            {
                // 查询最近的入厂记录有没有派车单号之类的
                var carNo = carInfo.CarNo;
                // 获取已存在的最最早的一条入场的记录
                var existsInRecord = db.ShippingRecords
                    .OrderBy(p => p.CreateDate)
                    .FirstOrDefault(p => p.ShipEndDate == null
                    && p.Direction == Core.Enums.PassagewayDirection.进
                    && p.CarNo == carNo);

                var defaultDate = DateTime.Parse("1990-01-01");

                // 再查询一次是否有已存在的默认进厂日期的 出厂记录
                if (existsInRecord == null)
                {
                    existsInRecord = db.ShippingRecords
                    .OrderBy(p => p.CreateDate)
                    .FirstOrDefault(p => p.ShipStartDate == defaultDate
                    && p.Direction == Core.Enums.PassagewayDirection.出
                    && p.CarNo == carNo);
                }

                if (existsInRecord != null)
                {
                    if (string.IsNullOrWhiteSpace(carInfo.Code))
                    {
                        carInfo.Code = existsInRecord.TicketCode;
                    }
                    if (string.IsNullOrWhiteSpace(carInfo.SendOrReceiveFlag))
                    {
                        carInfo.SendOrReceiveFlag = existsInRecord.SendOrReceiveFlag;
                    }
                }
            }

            var queryGoodsInfoWorker = CreateQueryGoodsInfoWorker(passageway, () => carInfo,
                                (car, cacheData) =>
                                {
                                    if (car == null)
                                    {
                                        ReleaseLocker();
                                        db.Dispose();
                                        return;
                                    }
                                    if (isNewCar)
                                    {
                                        _tempCarInfo = car;
                                    }
                                    carInfo = car;
                                    //todo-xq
                                    // 只有出厂且货物查不到的时候,才强制手动
                                    // 只有台账车才触发
                                    if (carInfo.TypeId < (long)CarTypeEnum.临时车
                                    && string.IsNullOrWhiteSpace(cacheData?.Data?.GoodsName)
                                    && passageway.Direction == PassagewayDirection.出)
                                    {
                                        autoPass = false;
                                    }

                                    enqueueTask.Start();
                                });

            if (isNewCar || !carInfo.Synchronized || carInfo.LocalTempLevelupToLedger)
            {
                // 上传车辆台账信息
                var uploadCarinfoWorker = CreateUploadCarInfoWorker(db,
                    () => carInfo,
                    () => passageway,
                    () => dbDevice,
                    (carUploadResult) =>
                    {
                        var isDefaultApi = _appointSvc.API is DefaultAppointAPI;
                        var msg = $"{carInfo.CarNo} 同步成功!";
                        if (!carUploadResult.Success)
                        {
                            if (!_appointSvc.API.IgoreErrorKeyWords.Any(p => carUploadResult.Message.Contains(p)))
                            {
                                msg = $"{carInfo.CarNo} 同步台账基础信息失败:{carUploadResult.Message}";
                            }
                            else
                            {
                                carUploadResult.Success = true;
                                carUploadResult.Message = string.Empty;
                            }
                            // 上传失败流程也要继续走,好让手动补全操作
                        }

                        _ctrl.Dispatcher.Invoke(() => ProcessMessages.Insert(0, msg));

                        // 如果是默认接口，同步标志无需更改
                        carInfo.Synchronized =
                        isDefaultApi ? false : carUploadResult.Success;

                        carInfo.SyncResult = isDefaultApi
                        ? string.Empty : carUploadResult.Message;

                        if (isNewCar)
                        {
                            // 这里要直接移除小程序的货物信息, 防止保存进本地数据
                            carInfo.InGoodsName = null;
                            carInfo.InGoodsVolume = 0;
                            carInfo.OutGoodsName = null;
                            carInfo.OutGoodsVolume = 0;
                            db.Cars.Add(carInfo);
                        }
                        else
                        {
                            // 查出来修改
                            var dbCarInfo = db.Cars.FirstOrDefault(p => p.Id == carInfo.Id);
                            dbCarInfo.Synchronized = carInfo.Synchronized;
                            dbCarInfo.SyncResult = carInfo.SyncResult;
                            dbCarInfo.UpdateDate = DateTime.Now;
                            db.Entry(dbCarInfo).State = EntityState.Modified;
                            carInfo = dbCarInfo;
                        }

                        try
                        {
                            db.SaveChanges();
                            queryGoodsInfoWorker.RunWorkerAsync();
                        }
                        catch (Exception e)
                        {
                            _ctrl.Dispatcher.Invoke(() =>
                            {
                                ProcessMessages.Insert(0, $"{carInfo.CarNo} 车辆台账信息保存失败.");
                                APILogs += $"\r\n{e.Message}";
                            });

                            ReleaseLocker();
                            db?.Dispose();

                            AJCommonDialog.ShowWindow(new AJCommonDialogViewModel.AJCommonDialogParams
                            {
                                Title = "提示",
                                Message = $"{carInfo.CarNo} 车辆台账信息保存失败.",
                                Detail = e.StackTrace
                            });
                        }


                    });

                uploadCarinfoWorker.RunWorkerAsync();
            }
            else
            {
                queryGoodsInfoWorker.RunWorkerAsync();
            }
        }

        private void ProcessNotLedger(
            Car carInfo,
            DeviceInfo deviceInfo,
            CarTypeEnum type,
            AJWPFAdmin.Core.Models.EF.Tables.Watchhouse watchhouse,
            AJWPFAdmin.Core.Models.EF.Tables.Passageway passageway,
            AJWPFAdmin.Core.Models.EF.Tables.Device dbDevice,
            bool isNewCar)
        {
            var db = DbService.GetDbContext(_cfgSvc);

            _ctrl.Dispatcher.Invoke(() =>
            {
                ProcessMessages.Insert(0, $"开始处理 {carInfo.CarNo}");
            });

            var enqueueTask = new Task(() =>
            {
                var entryConfig = GetEntryConfig(db);
                var autoPass = Direction == PassagewayDirection.进
                            ? entryConfig.CarTypeEnumCfgItems.Any(p => p.CarType == type && p.InChecked)
                            : entryConfig.CarTypeEnumCfgItems.Any(p => p.CarType == type && p.OutChecked);

                var record = CreateTempRecord(db, carInfo, deviceInfo, watchhouse, passageway, dbDevice, autoPass);

                record.CarSynchronized = record.Synchronized = true;

                _carArrivedEvent.Invoke(this, record);

                db.Dispose();
            });

            var queryGoodsInfoWorker = CreateQueryGoodsInfoWorker(passageway, () => carInfo,
                                (car, cacheData) =>
                                {
                                    if (car == null)
                                    {
                                        ReleaseLocker();
                                        db.Dispose();
                                        return;
                                    }
                                    if (isNewCar)
                                    {
                                        _tempCarInfo = car;
                                    }
                                    carInfo = car;

                                    enqueueTask.Start();
                                });

            queryGoodsInfoWorker.RunWorkerAsync();

        }

        private ShippingRecord CreateTempRecord(DbService db,
            Car carInfo,
            DeviceInfo deviceInfo,
            AJWPFAdmin.Core.Models.EF.Tables.Watchhouse watchhouse,
            AJWPFAdmin.Core.Models.EF.Tables.Passageway passageway,
            AJWPFAdmin.Core.Models.EF.Tables.Device dbDevice,
            bool autoPass)
        {

            var carIdentificationCfg = CarIdentificationConfigExt.GetCarIdentificationConfig(db);
            var entryConfig = GetEntryConfig(db);

            var images = new List<string> {
                    deviceInfo.CarIdentificationPlateResult.FullImgFile,
                    deviceInfo.CarIdentificationPlateResult.ClipImgFile
                };

            // 监控抓图
            if (_deviceSource != null && _deviceSource.Any(p => p.Direction == Direction && p.OnlyMonitor))
            {
                var monitors = _deviceSource.Where(p => p.Direction == Direction && p.OnlyMonitor);
                foreach (var monitor in monitors)
                {
                    images.Add(monitor.GetCapture(null, $"monitor_{monitor.WatchPosition}_{monitor.Direction}_{SnowFlake.GetInstance().GetUniqueShortId()}.jpg"));
                }
            }

            var now = DateTime.Now;
            var record = new ShippingRecord
            {
                Direction = Direction,
                CreateDate = now,
                DeviceCode = passageway.Direction == PassagewayDirection.进 ? dbDevice.Code : string.Empty,
                OutDeviceCode = passageway.Direction == PassagewayDirection.出 ? dbDevice.Code : string.Empty,
                DeviceId = passageway.Direction == PassagewayDirection.进 ? dbDevice.Id : 0,
                OutDeviceId = passageway.Direction == PassagewayDirection.出 ? dbDevice.Id : 0,
                PassagewayId = passageway.Direction == PassagewayDirection.进 ? dbDevice.PassagewayId : 0,
                OutPassagewayId = passageway.Direction == PassagewayDirection.出 ? dbDevice.PassagewayId : 0,
                PassagewayCode = passageway.Direction == PassagewayDirection.进 ? passageway.Code : string.Empty,
                OutPassagewayCode = passageway.Direction == PassagewayDirection.出 ? passageway.Code : string.Empty,
                PassagewayName = passageway.Direction == PassagewayDirection.进 ? passageway.Name : string.Empty,
                OutPassagewayName = passageway.Direction == PassagewayDirection.出 ? passageway.Name : string.Empty,
                ShipStartDate = now,
                WatchhouseId = passageway.Direction == PassagewayDirection.进 ? dbDevice.WatchhouseId : 0,
                OutWatchhouseId = passageway.Direction == PassagewayDirection.出 ? dbDevice.WatchhouseId : 0,
                WatchhouseCode = passageway.Direction == PassagewayDirection.进 ? watchhouse.Code : string.Empty,
                OutWatchhouseCode = passageway.Direction == PassagewayDirection.出 ? watchhouse.Code : string.Empty,
                WatchhouseName = passageway.Direction == PassagewayDirection.进 ? watchhouse.Name : string.Empty,
                OutWatchhouseName = passageway.Direction == PassagewayDirection.出 ? watchhouse.Name : string.Empty,
                // 直接赋值自动开闸 --阿吉 2024年12月11日19点47分
                AutoPass = true,
                IsConfigAutoPass = autoPass,
                InCaptureFile
                            = passageway.Direction == Core.Enums.PassagewayDirection.进
                            ? CommonUtil.AJSerializeObject(images.Where(p => !string.IsNullOrWhiteSpace(p))) : string.Empty,

                OutCaptureFile
                            = passageway.Direction == Core.Enums.PassagewayDirection.出
                            ? CommonUtil.AJSerializeObject(images.Where(p => !string.IsNullOrWhiteSpace(p))) : string.Empty,

                //EnablePassagewayStatistic = carType.EnablePassagewayStatistic,
                RecordNum = 0,

            };

            var recordProps = record.GetType().GetRuntimeProperties();
            var carProps = carInfo.GetType().GetRuntimeProperties();

            foreach (var recordProp in recordProps)
            {
                if (recordProp.Name == nameof(ShippingRecord.Id)
                    || recordProp.Name == nameof(ShippingRecord.CreateDate)
                    || recordProp.Name == nameof(ShippingRecord.UpdateDate)
                    || recordProp.Name == nameof(ShippingRecord.Synchronized))
                {
                    continue;
                }
                var carProp = carProps.FirstOrDefault(p => p.Name == recordProp.Name);
                if (carProp == null)
                {
                    continue;
                }
                recordProp.SetValue(record, carProp.GetValue(carInfo));
            }
            record.InGoodsName = string.IsNullOrWhiteSpace(carInfo.InGoodsName)
                ? "空车" : carInfo.InGoodsName;

            record.OutGoodsName = string.IsNullOrWhiteSpace(carInfo.OutGoodsName)
                ? "空车" : carInfo.OutGoodsName;

            record.InGoodsType = carInfo.InGoodsType;

            record.OutGoodsType = carInfo.OutGoodsType;

            record.SendOrReceiveFlag = carInfo.SendOrReceiveFlag;

            record.TicketCode = carInfo.Code;
            record.Sender = carInfo.Sender;
            record.Receiver = carInfo.Receiver;
            record.CarSynchronized = carInfo.Synchronized;

            // 复制临时文件到正式文件夹
            var imgArrayJsonStr = CopyTempImagesToSavePath(record, carIdentificationCfg);

            if (record.Direction == Core.Enums.PassagewayDirection.进)
            {
                record.InCaptureFile = imgArrayJsonStr;
            }
            else
            {
                record.OutCaptureFile = imgArrayJsonStr;
            }

            record.FreightUnit = entryConfig.DefaultFreightUnit;

            return record;
        }

        private Queue<DeviceInfo> _deviceNotifyQueue;

        private DelegateCommand _testAPI;
        public DelegateCommand TestAPI =>
            _testAPI ?? (_testAPI = new DelegateCommand(ExecuteTestAPI));

        async void ExecuteTestAPI()
        {
            var result = await _appointSvc.API.GetCarInfoAsync(new CarInfoParams
            {
                CarInfo = new Car
                {
                    CarNo = "京A12345"
                },
                Passageway = new Passageway
                {
                    Direction = PassagewayDirection.进
                },
                Device = new Device
                {
                    Code = "001"
                }
            });

            MessageBox.Show(CommonUtil.AJSerializeObject(result), "提示", MessageBoxButton.OK);
        }

        private DelegateCommand _testCar;
        public DelegateCommand TestCar =>
            _testCar ?? (_testCar = new DelegateCommand(ExecuteTestCar));

        void ExecuteTestCar()
        {
            var @params = new DialogParameters
            {
                { "params", new PromptDialogViewModel.PromptDialogParameter
                    {
                        Title = "请输入车牌号",
                        Required = true,
                        Placeholder = "车牌号"
                    }
                },
            };

            if (!_cfgSvc.IsAuthorized)
            {
                MessageBox.Show("测试未激活");
                return;
            }
            _dialogSvc.ShowDialog(nameof(PromptDialog), @params, r =>
            {
                if (r.Result == ButtonResult.OK)
                {
                    var dev = db.Devices.AsNoTracking().FirstOrDefault(p => p.IP == "192.168.1.100");
                    var psg = db.Passageways.AsNoTracking().FirstOrDefault(p => p.Id == dev.PassagewayId);
                    var wt = db.Watchhouses.AsNoTracking().FirstOrDefault(p => p.Id == dev.WatchhouseId);

                    OnDeviceNotify(new DeviceInfo
                    {
                        IP = dev.IP,
                        Code = dev.Code,
                        Id = dev.Id,
                        PassagewayId = psg.Id,
                        PassagewayCode = psg.Code,
                        PassagewayName = psg.Name,
                        WatchhouseId = wt.Id,
                        WatchhouseCode = wt.Code,
                        WatchhouseName = wt.Name,
                        Direction = psg.Direction,
                        CarIdentificationPlateResult = new CarIdentificationResult
                        {
                            CarNo = r.Parameters.GetValue<string>("value"),
                            FullImgFile = @"E:\MyPicture\16000R211-0.png"
                        }
                    });
                }
            });


        }

        private DelegateCommand<ConfirmPassCmdParameter?> _openShippinRecordDialogCmd;
        public DelegateCommand<ConfirmPassCmdParameter?> OpenShippingRecordDialogCmd =>
            _openShippinRecordDialogCmd ?? (_openShippinRecordDialogCmd = new DelegateCommand<ConfirmPassCmdParameter?>(ExecuteOpenShippinRecordDialogCmd, CanExecuteOpenShippingRecordDialogCmd));

        async void ExecuteOpenShippinRecordDialogCmd(ConfirmPassCmdParameter? parameter)
        {
            //todo-xq-检测台账/物资
            if (parameter == ConfirmPassCmdParameter.Dialog)
            {
                var @params = new DialogParameters
                {
                    { "data", CurRecord },
                };
                _isShippingRecordFormDialogOpen = true;
                _tempRecordProcessRemainCount = 0;
                _dialogSvc.ShowDialog(nameof(ShippingRecordForm), @params, async r =>
                {
                    if (r.Result == ButtonResult.OK)
                    {
                        r.Parameters.TryGetValue<ShippingRecord>("data", out var newRecord);
                        CurRecord = newRecord;
                        await ConfirmPassAsync(ConfirmPassCmdParameter.Dialog);
                    }
                    _isShippingRecordFormDialogOpen = false;
                });
                return;
            }
            await ConfirmPassAsync(parameter.GetValueOrDefault());
        }

        bool CanExecuteOpenShippingRecordDialogCmd(ConfirmPassCmdParameter? parameter)
        {
            var can = CurRecord != null && !string.IsNullOrWhiteSpace(CurRecord.CarNo);
            return can && !Saving;
        }

        private async Task ConfirmPassAsync(ConfirmPassCmdParameter parameter)
        {
            _tempRecordProcessWorking = true;
            _tempRecordProcessRemainCount = 30;

            var carTypeEnum = (CarTypeEnum)((int)CurRecord.TypeId);

            if (parameter == ConfirmPassCmdParameter.Ignore)
            {
                APILogs = "暂无接口日志";
                ProcessMessages.Clear();
                ProcessMessages.Add("欢迎使用");
                DeviceNotifyQueueCheckMessages.Clear();
                CurRecord = null;
                ToggleSaving();
                ReleaseLocker();
                return;
            }

            var db = DbService.GetDbContext(_cfgSvc);

            ToggleSaving(true);

            var carIdentificationConfig = CarIdentificationConfigExt.GetCarIdentificationConfig(db);

            var carNo = CurRecord.CarNo;

            _ctrl.Dispatcher.Invoke(() => ProcessMessages.Insert(0, $"{carNo} 正在处理..."));

            var newRecord = false;
            // 判断进出场
            if (CurRecord.Direction == Core.Enums.PassagewayDirection.进)
            {
                newRecord = await ProcessCarInAsync(db, carIdentificationConfig);
            }
            else
            {
                newRecord = await ProcessCarOutAsync(db, carIdentificationConfig);
            }

            if (newRecord)
            {
                db.ShippingRecords.Add(CurRecord);
            }

            if (CurRecord.BlackQueryError)
            {
                CurRecord.SyncResult = "黑名单接口查询异常或网络错误";
            }

            if (_tempCarInfo != null && CurRecord.TypeId == (long)CarTypeEnum.临时车)
            {
                // 如果是对话框参数, 有可能编辑过车辆信息,需要同步一下
                if (parameter == ConfirmPassCmdParameter.Dialog)
                {
                    MapDialogShippingRecirdToCar(ref _tempCarInfo);
                }
                db.Cars.Add(_tempCarInfo);
            }

            Action<bool> cleanHandler = (clearLogs) =>
            {
                if (clearLogs)
                {
                    APILogs = "暂无接口日志";
                }
                // 重置按钮状态, 可以继续弹窗手动修改
                ToggleSaving();
                // 释放锁
                ReleaseLocker();
                // 释放数据库连接
                db?.Dispose();
            };

            var saveRecordTask = new Task(() =>
            {
                try
                {
                    db.SaveChanges();
                }
                catch (Exception e)
                {
                    // 如果是其他问题， 写日志， 提示错误
                    _logger.Error($"{CurRecord.CarNo} 保存台账记录发生异常", e);

                    _ctrl.Dispatcher.Invoke(() =>
                    {
                        ProcessMessages.Insert(0, $"{CurRecord.CardNo} 处理失败");
                        APILogs += $"\r\n{e.Message}\r\n{e.StackTrace}";
                    });
                    if (_tempCarInfo != null)
                    {
                        // 清空临时车临时数据
                        _tempCarInfo = null;
                    }
                    cleanHandler(false);
                    return;
                }

                if (_tempCarInfo != null)
                {
                    // 清空临时车临时数据
                    _tempCarInfo = null;
                }

                //var ledLine4Text = record.EmissionStandard.ToString();

                //var now = DateTime.Now.Date;
                //var warehouseId = record.WatchhouseId;
                //var count = db.WarehousePassageRecords
                //    .Count(p => p.CreateDate.Date == now && p.WarehouseId == warehouseId && p.CarNo == carNo);

                //ledLine4Text = $"今日 {count}次";
                _ctrl.Dispatcher.Invoke(() => { ProcessMessages.Clear(); });

                var ledConfig = GetLEDConfig(db);
                OpenGate(CurRecord, ledConfig, true);
                DisplayLEDAsync(CurRecord, ledConfig, LEDTextScene.已通过);

                // 通知给其他需要刷新台账记录的组件
                _carIdentificationPassedEvent.Publish(CurRecord);

                // 出的时候推送图片到第三方 --阿吉 2024年11月7日09点43分
                if (CurRecord.Direction == PassagewayDirection.出
                && !string.IsNullOrWhiteSpace(carIdentificationConfig.PushUrl))
                {
                    var pushImagesWorker = new BackgroundWorker();
                    var files = new List<string>();

                    files.AddRange(CurRecord.GetCaptureFiles(PassagewayDirection.进).Select(p => carIdentificationConfig.CompleteFileUrl(CurRecord.CarNo, p)));

                    files.AddRange(CurRecord.GetCaptureFiles(PassagewayDirection.出).Select(p => carIdentificationConfig.CompleteFileUrl(CurRecord.CarNo, p)));

                    files = files.Where(p => !string.IsNullOrWhiteSpace(p)).Distinct().ToList();
                    var pushParams = new Dictionary<string, string>
                        {
                            {"carPlateNumber", CurRecord.CarNo},
                            {"bizCode", CurRecord.TicketCode},
                        };
                    pushImagesWorker.DoWork += (_, __) =>
                    {
                        carIdentificationConfig.Push(files, pushParams, (log) =>
                        {
                            _ctrl.Dispatcher.Invoke(() =>
                            {
                                APILogs += $"\r\n{log}\r\n";
                            });
                        });
                    };
                    pushImagesWorker.RunWorkerCompleted += (_, __) =>
                    {

                    };
                    pushImagesWorker.RunWorkerAsync();
                }

                // 上传台账
                if (carTypeEnum == CarTypeEnum.进出厂车 || carTypeEnum == CarTypeEnum.固定车)
                {
                    // 如果是黑名单查询失败, 那也不走上传台账了, 直接手动处理 --阿吉 2025年4月9日12点14分
                    if (CurRecord.BlackQueryError)
                    {
                        _ctrl.Dispatcher.Invoke(() =>
                        {
                            APILogs += $"\r\n车辆黑名单查询异常,请手动去管理端处理上传\r\n";
                            ProcessMessages.Insert(0, $"{carNo} n车辆黑名单查询异常");
                        });
                        CurRecord = null;
                        cleanHandler(false);
                        return;
                    }
                    var isDefaultApi = _appointSvc.API is DefaultAppointAPI;
                    // 如果车辆信息同步失败且也不是默认接口,那也没必要上传进出台账，直接通过主流程，交给人工或者定时器处理
                    if (!CurRecord.CarSynchronized && !isDefaultApi)
                    {
                        _ctrl.Dispatcher.Invoke(() =>
                        {
                            APILogs += $"\r\n车辆信息同步失败，将无法继续上传进出记录，后续由管理端或定时器自动处理\r\n";
                            ProcessMessages.Insert(0, $"{carNo} 车辆信息同步失败，直接通行，后续处理");
                        });
                        CurRecord = null;
                        cleanHandler(false);
                        return;
                    }

                    //必须重新查出来保存, 否则报并发错误
                    var shippingRecordDb = DbService.GetDbContext(_cfgSvc);
                    var rid = CurRecord.Id;
                    var dbRecord = shippingRecordDb.ShippingRecords.FirstOrDefault(p => p.Id == rid);

                    var uploadRecordWorker = CreateUploadRecordWorker((uploadRet) =>
                    {
                        if (!uploadRet.Success)
                        {
                            if (uploadRet.Message.Contains("车辆信息不存在"))
                            {
                                var dbCar = shippingRecordDb.Cars.FirstOrDefault(p => p.CarNo == carNo);
                                dbCar.Synchronized = false;
                                dbCar.SyncResult = uploadRet.Message;
                                dbCar.UpdateDate = DateTime.Now;
                                db.Entry(dbCar).State = EntityState.Modified;
                            }

                            if (!_appointSvc.API.IgoreErrorKeyWords.Any(p => uploadRet.Message.Contains(p)))
                            {
                                dbRecord.SyncResult = $"{uploadRet.Message}";
                            }
                        }
                        else
                        {
                            dbRecord.SyncResult = string.Empty;
                        }

                        var isDefaultApi = _appointSvc.API is DefaultAppointAPI;

                        // 如果是默认接口，上传标志不做任何更改
                        if (isDefaultApi)
                        {
                            dbRecord.Synchronized = false;
                            dbRecord.SyncResult = string.Empty;
                        }
                        else if (_appointSvc.API.KeyDescription == "青岛" || _appointSvc.API.KeyDescription == "赣州" || _appointSvc.API.KeyDescription == "成都")
                        {
                            dbRecord.Synchronized = true;
                            dbRecord.SyncResult = uploadRet.Success ? string.Empty : uploadRet.Message;
                        }
                        else
                        {
                            // 从返回结果读取过车记录和物资的上传次数重新更新
                            var retCopy = uploadRet.Attach as UploadShippingRecordResponse;
                            retCopy ??= new UploadShippingRecordResponse();

                            if (!string.IsNullOrWhiteSpace(retCopy.VideoFile))
                            {
                                if (dbRecord.Direction == PassagewayDirection.进)
                                {
                                    dbRecord.InVideoCaptureFile = Path.GetFileName(retCopy.VideoFile);
                                }
                                else
                                {
                                    dbRecord.OutVideoCaptureFile = Path.GetFileName(retCopy.VideoFile);
                                }
                            }

                            dbRecord.Synchronized = retCopy.RecordNum > 0 || retCopy.GoodsNum > 0;

                            dbRecord.SyncResult = uploadRet.Success
                            ? string.Empty : uploadRet.Message;

                            dbRecord.RecordNum += retCopy.RecordNum;
                            dbRecord.GoodsNum += retCopy.GoodsNum;
                        }
                        dbRecord.UpdateDate = DateTime.Now;
                        shippingRecordDb.Entry(dbRecord).State = EntityState.Modified;
                        try
                        {
                            shippingRecordDb.SaveChanges();
                        }
                        catch (Exception e)
                        {
                            _ctrl.Dispatcher.Invoke(() =>
                            {
                                APILogs += $"\r\n{e.Message}\r\n{e.StackTrace}";
                            });

                        }
                        finally
                        {
                            shippingRecordDb?.Dispose();
                        }

                        if (parameter == ConfirmPassCmdParameter.ForcePass || dbRecord.Synchronized || isDefaultApi)
                        {
                            CurRecord = null;
                        }
                        else
                        {
                            CurRecord = dbRecord;
                        }
                        var msg = dbRecord.Synchronized || isDefaultApi ? "台账上传成功" : "台账上传失败";
                        _ctrl.Dispatcher.Invoke(() => ProcessMessages.Insert(0, $"{carNo} {msg}"));
                        cleanHandler(dbRecord.Synchronized || isDefaultApi);
                    });

                    uploadRecordWorker.RunWorkerAsync(dbRecord);

                }
                else
                {
                    CurRecord = null;

                    cleanHandler(true);
                }

            });

            var carInfo = await db.Cars.AsNoTracking().FirstOrDefaultAsync(p => p.CarNo == carNo);

            if (parameter == ConfirmPassCmdParameter.Dialog)
            {
                if (carInfo != null)
                {
                    MapDialogShippingRecirdToCar(ref carInfo);
                    carInfo.UpdateDate = DateTime.Now;
                    db.Entry(carInfo).State = EntityState.Modified;
                    db.SaveChanges();
                }
            }

            // 如果进出厂车/固定车/非道路移动机械到这一步车辆信息同步失败,且是对话框参数 则要重新上传一次,因为有可能弹窗里面手动更改了数据
            if ((carTypeEnum == CarTypeEnum.进出厂车
                || carTypeEnum == CarTypeEnum.固定车
                || carTypeEnum == CarTypeEnum.非道路移动机械)
                && !CurRecord.CarSynchronized)
            {
                if (carInfo == null)
                {
                    _ctrl.Dispatcher.Invoke(() =>
                    {
                        APILogs += $"\r\n本地车辆记录缺失,可能被删除,请重新过车";
                        ProcessMessages.Insert(0, $"{carNo} 通行失败!");
                    });
                    cleanHandler(false);
                    return;
                }

                var uploadCarInfoWorker = CreateUploadCarInfoWorker(db,
                    () => carInfo,
                    () => new Passageway { Direction = CurRecord.Direction },
                    () => new Device
                    {
                        Code = CurRecord.Direction == PassagewayDirection.进
                                ? CurRecord.DeviceCode : CurRecord.OutDeviceCode
                    },
                    (carUploadResult) =>
                    {
                        var msg = $"{carNo} 同步成功!";
                        if (!carUploadResult.Success)
                        {
                            if (!_appointSvc.API.IgoreErrorKeyWords.Any(p => carUploadResult.Message.Contains(p)))
                            {
                                msg = $"{carNo} 同步台账基础信息失败:{carUploadResult.Message}";
                            }
                            // 上传失败流程也要继续走,好让手动补全操作
                        }

                        _ctrl.Dispatcher.Invoke(() => ProcessMessages.Insert(0, msg));
                        using var carDb = DbService.GetDbContext(_cfgSvc);
                        var dbCar = carDb.Cars.FirstOrDefault(p => p.Id == carInfo.Id);

                        // 把表单数据同步给数据库车辆数据
                        CommonUtil.CopyPropertyValues(dbCar, carInfo);

                        var isDefaultApi = _appointSvc.API is DefaultAppointAPI;
                        dbCar.Synchronized = carUploadResult.Success;
                        dbCar.SyncResult = carUploadResult.Message;
                        CurRecord.CarSynchronized = CurRecord.CarSynchronized = isDefaultApi
                        ? false : carUploadResult.Success;

                        CurRecord.SyncResult = CurRecord.SyncResult = isDefaultApi
                        ? string.Empty : carUploadResult.Message;
                        dbCar.UpdateDate = DateTime.Now;
                        carDb.Entry(dbCar).State = EntityState.Modified;

                        try
                        {
                            carDb.SaveChanges();
                        }
                        catch (Exception e)
                        {
                            _ctrl.Dispatcher.Invoke(() =>
                            {
                                ProcessMessages.Insert(0, $"{carInfo.CarNo} 车辆台账信息保存失败.");
                                APILogs += $"\r\n{e.Message}\r\n{e.StackTrace}";
                            });
                        }

                        carInfo = dbCar;

                        CurRecord.CarSynchronized = CurRecord.CarSynchronized = carInfo.Synchronized;
                        // 不管车辆基础台账同步是否生成，始终进行下一步，免得卡流程
                        saveRecordTask.Start();

                    });
                uploadCarInfoWorker.RunWorkerAsync();
            }
            else
            {
                saveRecordTask.Start();
            }
        }

        private void MapDialogShippingRecirdToCar(ref Car carInfo)
        {
            // 用台账信息的车辆信息覆盖一下车辆的
            var carProps = carInfo.GetType().GetRuntimeProperties();
            var recordProps = CurRecord.GetType().GetRuntimeProperties();
            foreach (var carProp in carProps)
            {
                if (carProp.Name == nameof(Car.Id)
                || carProp.Name == nameof(Car.CreateDate)
                || carProp.Name == nameof(Car.UpdateDate)
                || carProp.Name == nameof(Car.Synchronized))
                {
                    continue;
                }
                var recordProp = recordProps.FirstOrDefault(p => p.Name == carProp.Name);
                if (recordProp == null)
                {
                    continue;
                }
                carProp.SetValue(carInfo, recordProp.GetValue(CurRecord));
            }
        }

        private void ToggleSaving(bool saving = false)
        {
            // 重置按钮状态, 可以继续弹窗手动修改
            Saving = saving;
            OpenShippingRecordDialogCmd.RaiseCanExecuteChanged();
        }

        private void ReleaseLocker()
        {
            if (_tempRecordProcessWorking)
            {
                _tempRecordProcessWorking = false;
                _tempRecordProcessRemainCount = 0;
            }
        }

        private LEDConfig GetLEDConfig(DbService db)
        {
            var ledConfig = CommonUtil.TryGetJSONObject<LEDConfig>(db.SystemConfigDictionaries
                    .Where(p => p.Key == SystemConfigKey.LEDConfig)
                    .Select(p => p.StringValue).FirstOrDefault()) ?? new LEDConfig();

            if (ledConfig.EntranceTextArray == null || ledConfig.ExitTextArray == null)
            {
                ledConfig.Init();
            }
            ledConfig.EQLEDs ??= new List<Core.HardwareSDKS.EQLED.EQLEDControlConfigModel>();
            return ledConfig;
        }

        private void TriggerAlarm(ShippingRecord record)
        {
            // 给设备发送开闸事件,由设备进行开闸逻辑处理
            var devId = this.Direction == PassagewayDirection.进
                ? record.DeviceId : record.OutDeviceId.GetValueOrDefault();

            var passagewayId = this.Direction == PassagewayDirection.进
                ? record.PassagewayId : record.OutPassagewayId.GetValueOrDefault();

            _deviceReceiveCmdEvent.Publish(new DeviceReceiveCmdEventArgs
            {
                DeviceId = devId,
                PassagewayId = passagewayId,
                Type = DeviceReceiveCmdType.IO2报警,
            });
            using var db = DbService.GetDbContext(_cfgSvc);
            DisplayLEDAsync(record, GetLEDConfig(db), LEDTextScene.报警);

            _ctrl.Dispatcher.Invoke(() =>
            ProcessMessages.Insert(0, $"{record.CarNo} 触发报警"));
        }

        private void OpenGate(ShippingRecord record,
            LEDConfig ledConfig,
            bool open)
        {
            //var ledConfig = GetLEDConfig();

            var scene = open ? LEDTextScene.已通过 : LEDTextScene.正在通行;

            var cameraProcessArray = ledConfig.FormatLEDTextArray(scene, Direction,
                record, true,
                customFormatHandle: _customLEDTextFormatHandle);

            // 给设备发送开闸事件,由设备进行开闸逻辑处理
            var devId = this.Direction == PassagewayDirection.进
                ? record.DeviceId : record.OutDeviceId.GetValueOrDefault();

            var passagewayId = this.Direction == PassagewayDirection.进
                ? record.PassagewayId : record.OutPassagewayId.GetValueOrDefault();

            _logger.Info($"触发开闸事件,通知设备开闸:方向:{this.Direction};设备id:{devId};是否开:{open},车号:{record.CarNo}");

            _deviceReceiveCmdEvent.Publish(new DeviceReceiveCmdEventArgs
            {
                DeviceId = devId,
                PassagewayId = passagewayId,
                Type = DeviceReceiveCmdType.开闸,
                Data = new DeviceOpenGateCmdParameter
                {
                    Direction = record.Direction,
                    Open = open,
                    TTSText = $"{record.CarNo}允许通行",
                    LEDTextLines = cameraProcessArray
                }
            });

            _ctrl.Dispatcher.Invoke(() => ProcessMessages.Insert(0, open ? $"{record.CarNo} 已通过" : $"{record.CarNo} 请稍后..."));
        }

        private async void DisplayLEDAsync(ShippingRecord record, LEDConfig ledConfig, LEDTextScene scene)
        {
            var passagewayId = this.Direction == PassagewayDirection.进
                ? record.PassagewayId : record.OutPassagewayId.GetValueOrDefault();

            var ledProcessArray = ledConfig.FormatLEDTextArray(scene, Direction,
                record, false,
                customFormatHandle: _customLEDTextFormatHandle);

            using var db = DbService.GetDbContext(_cfgSvc);
            var passageway = await db.Passageways.AsNoTracking().FirstOrDefaultAsync(p => p.Id == passagewayId);

            if (passageway == null)
            {
                var errMsg = "发送数据到EQLED失败:通道数据缺失";
                _logger.Error(errMsg);
                //_ctrl.Dispatcher.Invoke(() => ProcessMessages.Insert(0, errMsg));
                return;
            }
            await Task.Run(() =>
            {
                ledConfig.DisplayToLEDs(
                scene,
                passageway, record, false, ledProcessArray, (logs) =>
                {
                    _logger.Debug(logs);
                });
            });
        }

        private async Task<bool> ProcessCarInAsync(DbService db, CarIdentificationConfig carIdentificationConfig)
        {
            var result = false;
            var carNo = CurRecord.CarNo;
            var isExitRecord = false;
            var inRecord = await db.ShippingRecords
                        .OrderBy(p => p.CreateDate)
                        .FirstOrDefaultAsync(p => p.ShipEndDate == null
                        && p.Direction == Core.Enums.PassagewayDirection.进
                        && p.CarNo == carNo);

            var defaultDate = DateTime.Parse("1990-01-01");

            // 再查询一次是否有出厂记录
            if (inRecord == null)
            {
                inRecord = db.ShippingRecords
                .OrderBy(p => p.CreateDate)
                .FirstOrDefault(p => p.ShipEndDate == defaultDate
                && p.Direction == Core.Enums.PassagewayDirection.出
                && p.CarNo == carNo);

                isExitRecord = inRecord != null;
            }

            if (inRecord != null)
            {
                #region 这些数据保持
                if (isExitRecord)
                {
                    CurRecord.ShipEndDate = inRecord.ShipEndDate;
                    CurRecord.OutCaptureFile = inRecord.OutCaptureFile;

                    if ((string.IsNullOrWhiteSpace(CurRecord.OutGoodsName) || "空车" == CurRecord.OutGoodsName)
                        && "空车" != inRecord.OutGoodsName)
                    {
                        CurRecord.OutGoodsName = inRecord.OutGoodsName;
                        CurRecord.OutGoodsVolume = inRecord.OutGoodsVolume;
                        CurRecord.OutGoodsType = inRecord.OutGoodsType;
                    }

                    CurRecord.OutWatchhouseId = inRecord.OutWatchhouseId;
                    CurRecord.OutWatchhouseCode = inRecord.OutWatchhouseCode;
                    CurRecord.OutWatchhouseName = inRecord.OutWatchhouseName;
                    CurRecord.OutPassagewayId = inRecord.OutPassagewayId;
                    CurRecord.OutPassagewayCode = inRecord.OutPassagewayCode;
                    CurRecord.OutPassagewayName = inRecord.OutPassagewayName;
                    CurRecord.OutDeviceId = inRecord.OutDeviceId;
                    CurRecord.OutDeviceCode = inRecord.OutDeviceCode;
                }

                #endregion

                CommonUtil.CopyPropertyValues(inRecord, CurRecord);
                inRecord.ShipStartDate = CurRecord.ShipStartDate = DateTime.Now;
                inRecord.UpdateDate = DateTime.Now;
                if (string.IsNullOrWhiteSpace(inRecord.InGoodsName) || "空车" == inRecord.InGoodsName)
                {
                    inRecord.InGoodsVolume = 0;
                    inRecord.InGoodsType = string.Empty;
                }
                if (string.IsNullOrWhiteSpace(inRecord.OutGoodsName) || "空车" == inRecord.OutGoodsName)
                {
                    inRecord.OutGoodsVolume = 0;
                    inRecord.OutGoodsType = string.Empty;
                }

                #region 判断是否大宗 --阿吉 2025年6月26日11点07分

                var goodsName = inRecord.InGoodsName;

                inRecord.InGoodsIsBulkCargo = await db.Goods
                    .Where(p => p.Name == goodsName).Select(p => p.IsBulkCargo).FirstOrDefaultAsync();

                #endregion

                inRecord.AutoPass = true;
                db.Entry(inRecord).State = EntityState.Modified;
                await db.SaveChangesAsync();
                CurRecord.Id = inRecord.Id;
            }
            else
            {
                CurRecord.Id = YitIdHelper.NextId();
                CurRecord.ShipStartDate = DateTime.Now;
                if (string.IsNullOrWhiteSpace(CurRecord.InGoodsName) || "空车" == CurRecord.InGoodsName)
                {
                    CurRecord.InGoodsVolume = 0;
                    CurRecord.InGoodsType = string.Empty;
                }
                if (string.IsNullOrWhiteSpace(CurRecord.OutGoodsName) || "空车" == CurRecord.OutGoodsName)
                {
                    CurRecord.OutGoodsVolume = 0;
                    CurRecord.OutGoodsType = string.Empty;
                }

                #region 判断是否大宗 --阿吉 2025年6月26日11点07分

                var goodsName = CurRecord.InGoodsName;

                CurRecord.InGoodsIsBulkCargo = await db.Goods
                    .Where(p => p.Name == goodsName).Select(p => p.IsBulkCargo).FirstOrDefaultAsync();

                #endregion

                CurRecord.AutoPass = true;
                result = true;
            }

            return result;
        }

        private async Task<bool> ProcessCarOutAsync(DbService db, CarIdentificationConfig carIdentificationConfig)
        {
            var result = false;
            var carNo = CurRecord.CarNo;
            var isRepeatRecord = false;
            // 获取已存在的最最早的一条入场的记录
            var existsInRecord = await db.ShippingRecords
                .OrderBy(p => p.CreateDate)
                .FirstOrDefaultAsync(p => p.ShipEndDate == null
                && p.Direction == Core.Enums.PassagewayDirection.进
                && p.CarNo == carNo);

            var defaultDate = DateTime.Parse("1990-01-01");

            // 再查询一次是否有已存在的默认进厂日期的 出厂记录
            if (existsInRecord == null)
            {
                existsInRecord = db.ShippingRecords
                .OrderBy(p => p.CreateDate)
                .FirstOrDefault(p => p.ShipStartDate == defaultDate
                && p.Direction == Core.Enums.PassagewayDirection.出
                && p.CarNo == carNo);

                isRepeatRecord = existsInRecord != null;
            }

            if (existsInRecord == null)
            {
                // 如果 没有,则创建一条, 进厂日期 是  1990-01-01
                CurRecord.Id = YitIdHelper.NextId();
                CurRecord.ShipStartDate = defaultDate;
                CurRecord.ShipEndDate = DateTime.Now;

                if (string.IsNullOrWhiteSpace(CurRecord.InGoodsName) || "空车" == CurRecord.InGoodsName)
                {
                    CurRecord.InGoodsVolume = 0;
                    CurRecord.InGoodsType = string.Empty;
                }
                if (string.IsNullOrWhiteSpace(CurRecord.OutGoodsName) || "空车" == CurRecord.OutGoodsName)
                {
                    CurRecord.OutGoodsVolume = 0;
                    CurRecord.OutGoodsType = string.Empty;
                }

                #region 判断是否大宗 --阿吉 2025年6月26日11点07分

                var goodsName = CurRecord.InGoodsName;

                CurRecord.InGoodsIsBulkCargo = await db.Goods
                    .Where(p => p.Name == goodsName).Select(p => p.IsBulkCargo).FirstOrDefaultAsync();

                #endregion

                CurRecord.AutoPass = true;
                //db.ShippingRecords.Add(CurRecord);
                result = true;
            }
            else
            {

                #region 这些数据保持

                if (!isRepeatRecord)
                {
                    CurRecord.ShipStartDate = existsInRecord.ShipStartDate;
                    CurRecord.InCaptureFile = existsInRecord.InCaptureFile;

                    if ((string.IsNullOrWhiteSpace(CurRecord.InGoodsName) || "空车" == CurRecord.InGoodsName)
                        && "空车" != existsInRecord.InGoodsName)
                    {
                        CurRecord.InGoodsName = existsInRecord.InGoodsName;
                        CurRecord.InGoodsVolume = existsInRecord.InGoodsVolume;
                        CurRecord.InGoodsType = existsInRecord.InGoodsType;
                    }

                    CurRecord.WatchhouseId = existsInRecord.WatchhouseId;
                    CurRecord.WatchhouseCode = existsInRecord.WatchhouseCode;
                    CurRecord.WatchhouseName = existsInRecord.WatchhouseName;
                    CurRecord.PassagewayId = existsInRecord.PassagewayId;
                    CurRecord.PassagewayCode = existsInRecord.PassagewayCode;
                    CurRecord.PassagewayName = existsInRecord.PassagewayName;
                    CurRecord.DeviceId = existsInRecord.DeviceId;
                    CurRecord.DeviceCode = existsInRecord.DeviceCode;
                }

                #endregion

                CommonUtil.CopyPropertyValues(existsInRecord, CurRecord);

                CurRecord.ShipEndDate = existsInRecord.ShipEndDate = DateTime.Now;
                existsInRecord.UpdateDate = DateTime.Now;
                if (string.IsNullOrWhiteSpace(existsInRecord.InGoodsName) || "空车" == existsInRecord.InGoodsName)
                {
                    existsInRecord.InGoodsVolume = 0;
                    existsInRecord.InGoodsType = string.Empty;
                }
                if (string.IsNullOrWhiteSpace(existsInRecord.OutGoodsName) || "空车" == existsInRecord.OutGoodsName)
                {
                    existsInRecord.OutGoodsVolume = 0;
                    existsInRecord.OutGoodsType = string.Empty;
                }

                #region 判断是否大宗 --阿吉 2025年6月26日11点07分

                var goodsName = existsInRecord.InGoodsName;

                existsInRecord.InGoodsIsBulkCargo = await db.Goods
                    .Where(p => p.Name == goodsName).Select(p => p.IsBulkCargo).FirstOrDefaultAsync();

                #endregion

                existsInRecord.AutoPass = true;
                db.Entry(existsInRecord).State = EntityState.Modified;

                await db.SaveChangesAsync();

                CurRecord.Id = existsInRecord.Id;
            }
            return result;
        }

        private void CheckDeviceNotifyQueue()
        {
            while (true)
            {
                var now = DateTime.Now.ToString("mm:ss");
                _ctrl?.Dispatcher.Invoke(() =>
                {
                    if (DeviceNotifyQueueCheckMessages.Count > 60)
                    {
                        DeviceNotifyQueueCheckMessages.Clear();
                    }
                });

                QueueCount = _deviceNotifyQueue.Count;
                try
                {
                    _ctrl?.Dispatcher.Invoke(() =>
                    {
                        OpenShippingRecordDialogCmd.RaiseCanExecuteChanged();
                    });
                }
                catch
                {
                }

                if (_tempRecordProcessWorking)
                {
                    _ctrl?.Dispatcher.Invoke(() =>
                    {
                        DeviceNotifyQueueCheckMessages.Insert(0, $"【{now}】线程锁定正在处理中...{CurRecord?.CarNo ?? string.Empty};剩余检测次数:{_tempRecordProcessRemainCount}");
                    });

                    if (_tempRecordProcessRemainCount > 0)
                    {
                        Thread.Sleep(1000);
                        _tempRecordProcessRemainCount--;
                        continue;
                    }

                }

                if (CurRecord != null)
                {
                    _ctrl?.Dispatcher.Invoke(() =>
                    {
                        DeviceNotifyQueueCheckMessages.Insert(0, $"【{now}】{CurRecord.CarNo} 等待处理");
                    });
                    // 如果没有打开，则顶掉当前车
                    if (!_isShippingRecordFormDialogOpen)
                    {
                        _ctrl?.Dispatcher.Invoke(() =>
                        {
                            DeviceNotifyQueueCheckMessages.Insert(0, $"【{now}】{CurRecord.CarNo} 未打开弹窗,尝试顶掉");
                        });
                        // 并且队列来了一个车，则当前车直接通过(忽略),让下一个车识别通知进来
                        if (_deviceNotifyQueue.Count > 0)
                        {
                            _ctrl?.Dispatcher.Invoke(() =>
                            {
                                DeviceNotifyQueueCheckMessages.Insert(0, $"【{now}】队列存在车辆,立即顶掉");
                            });
                            var carTypeEnum = (CarTypeEnum)CurRecord.TypeId;

                            var cmdParam = ConfirmPassCmdParameter.Ignore;

                            //if (_appointSvc.API is TangShanAppointAPI
                            //    && (carTypeEnum == CarTypeEnum.进出厂车 || carTypeEnum == CarTypeEnum.固定车))
                            //{
                            //    cmdParam = ConfirmPassCmdParameter.ForcePass;
                            //}
                            try
                            {
                                _ctrl.Dispatcher.Invoke(async () =>
                                {
                                    try
                                    {
                                        await ConfirmPassAsync(cmdParam);
                                    }
                                    catch (Exception e)
                                    {
                                        _logger?.Error($"车辆识别顶车流程异常:{e.Message}", e);
                                        ReleaseLocker();
                                    }
                                });
                            }
                            catch
                            {

                            }

                            Thread.Sleep(1000);
                            continue;
                        }
                        _ctrl?.Dispatcher.Invoke(() =>
                        {
                            DeviceNotifyQueueCheckMessages.Insert(0, $"【{now}】队列中不存在任何车辆,将保持 {CurRecord.CarNo},进入下一轮检测");
                        });
                    }
                    else
                    {
                        _ctrl?.Dispatcher.Invoke(() =>
                        {
                            DeviceNotifyQueueCheckMessages.Insert(0, $"【{now}】已打开弹窗,将保持 {CurRecord.CarNo},进入下一轮检测");
                        });
                    }

                    Thread.Sleep(1000);
                    continue;
                }

                if (_deviceNotifyQueue.Count > 0)
                {
                    _ctrl?.Dispatcher.Invoke(() =>
                    {
                        DeviceNotifyQueueCheckMessages.Insert(0, $"【{now}】队列中存在车辆,出队列");
                    });
                    try
                    {
                        OnDeviceNotifyDequeue(_deviceNotifyQueue.Dequeue());
                    }
                    catch (Exception e)
                    {
                        _logger?.Error($"车辆识别出队列异常:{e.Message}", e);
                        ReleaseLocker();
                    }
                }
                else
                {
                    _ctrl?.Dispatcher.Invoke(() =>
                    {
                        DeviceNotifyQueueCheckMessages.Insert(0, $"【{now}】队列空闲中,进入下一轮检测");
                    });
                }

                Thread.Sleep(1000);
            }
        }

        private string CopyTempImagesToSavePath(ShippingRecord record,
            CarIdentificationConfig carIdentificationConfig)
        {
            var targetCarNoImagesPath = CarIdentificationConfig.GetDefaultSavePath();

            if (!string.IsNullOrWhiteSpace(carIdentificationConfig?.ImageSavePath))
            {
                targetCarNoImagesPath = carIdentificationConfig.ImageSavePath;
            }

            var imgJsonArrayStr = record.Direction == Core.Enums.PassagewayDirection.进
                ? record.InCaptureFile : record.OutCaptureFile;

            if (carIdentificationConfig.Type == CarIdentificationConfig.StoreType.本地)
            {
                try
                {
                    if (!Directory.Exists(targetCarNoImagesPath))
                    {
                        Directory.CreateDirectory(targetCarNoImagesPath);
                    }
                }
                catch (Exception ex)
                {
                    // 如果创建失败, 说明可能是共享文件夹没配置好之类的,那就只能使用本地数据
                    _logger?.Error($"CopyTempImagesToSavePath异常--文件路径-{targetCarNoImagesPath}-异常日志:{ex.Message}", ex);
                    return imgJsonArrayStr;
                }
            }


            var list = CommonUtil.TryGetJSONObject<List<string>>(imgJsonArrayStr) ?? new List<string>();
            Action<string> webUploadLogHandler = (log) =>
            {
                _logger.Error(log);
                APILogs += $"{log}\r\n";
            };
            if (list.Count > 0)
            {
                for (int i = 0; i < list.Count; i++)
                {
                    var fName = Path.GetFileNameWithoutExtension(list[i]);
                    if (fName.StartsWith("full")
                        || fName.StartsWith("clip"))
                    {
                        var fNameArray = fName.Split('_').Distinct().ToList();
                        fNameArray.Insert(1, record.Direction.ToString());
                        fName = string.Join("_", fNameArray);
                    }
                    var ext = Path.GetExtension(list[i]);

                    if (carIdentificationConfig.Type == CarIdentificationConfig.StoreType.本地)
                    {
                        var newFile = Path.Combine(targetCarNoImagesPath, record.CarNo, $"{fName}{ext}");

                        try
                        {
                            if (CommonUtil.TryCopyFileWithNoTry(list[i], newFile))
                            {
                                // 删除临时文件
                                File.Delete(list[i]);
                                // 只存储文件名了,后续读取图片全部动态拼接, {管理端配置路径}/{车牌号}/xxx.jpg --阿吉 2025年1月10日10点20分
                                list[i] = Path.GetFileName(newFile);
                            }
                        }
                        catch (Exception ex)
                        {
                            _logger?.Error($"CopyTempImagesToSavePath-TryCopyFileWithNoTry异常--文件名称-{newFile}-异常日志:{ex.Message}", ex);
                        }
                    }
                    else
                    {
                        var (file, success) = carIdentificationConfig.UploadToWebService(list[i],
                            new Dictionary<string, string>
                        {
                            {"category",record.CarNo },
                        }, webUploadLogHandler);
                        if (success)
                        {
                            // 删除临时文件
                            File.Delete(list[i]);
                            list[i] = file;
                        }
                    }
                }
            }

            return CommonUtil.AJSerializeObject(list);
        }

        public class CarIdentificationPassedEvent : PubSubEvent<ShippingRecord>
        {

        }

        public class CarImageDownloadProcessMap
        {
            public string Desc { get; set; }

            public string PropertyName { get; set; }

            public string File { get; private set; }

            public void Init(Car carInfo)
            {
                File = carInfo.GetFileOfPropertyName(PropertyName);
            }

            public void FillFileToEntity(ref Car carInfo, string fileName)
            {
                if (!string.IsNullOrEmpty(fileName))
                {
                    fileName =
                        CommonUtil.AJSerializeObject(new string[] { Path.GetFileName(fileName) });
                }
                carInfo.GetType().GetProperty(PropertyName).SetValue(carInfo, fileName);
            }
        }
    }
}
